1
0
Fork 0
mirror of https://gitlab.com/Shinobi-Systems/ShinobiCE.git synced 2025-03-09 15:40:15 +00:00
This commit is contained in:
Moe 2019-10-26 13:34:02 -07:00
parent ef40f3f231
commit 167603dfb0
118 changed files with 16152 additions and 5441 deletions

View file

@ -5,109 +5,184 @@ module.exports = function(s,config,lang){
s.superUsersApi = {}
s.factorAuth = {}
s.failedLoginAttempts = {}
//auth handler
//params = parameters
//cb = callback
//res = response, only needed for express (http server)
//request = request, only needed for express (http server)
s.auth = function(params,cb,res,req){
//
var getUserByUid = function(params,columns,callback){
if(!columns)columns = '*'
s.sqlQuery(`SELECT ${columns} FROM Users WHERE uid=? AND ke=?`,[params.uid,params.ke],function(err,r){
if(!r)r = []
var user = r[0]
callback(err,user)
})
}
var getUserBySessionKey = function(params,callback){
s.sqlQuery('SELECT * FROM Users WHERE auth=? AND ke=?',[params.auth,params.ke],function(err,r){
if(!r)r = []
var user = r[0]
callback(err,user)
})
}
var loginWithUsernameAndPassword = function(params,columns,callback){
if(!columns)columns = '*'
s.sqlQuery(`SELECT ${columns} FROM Users WHERE mail=? AND (pass=? OR pass=?) LIMIT 1`,[params.username,params.password,s.createHash(params.password)],function(err,r){
if(!r)r = []
var user = r[0]
callback(err,user)
})
}
var getApiKey = function(params,columns,callback){
if(!columns)columns = '*'
s.sqlQuery(`SELECT ${columns} FROM API WHERE code=? AND ke=?`,[params.auth,params.ke],function(err,r){
if(!r)r = []
var apiKey = r[0]
callback(err,apiKey)
})
}
var loginWithApiKey = function(params,callback){
getApiKey(params,'*',function(err,apiKey){
var isSessionKey = false
if(apiKey){
var sessionKey = params.auth
createSession(apiKey,{
auth: sessionKey,
permissions: s.parseJSON(apiKey.details),
details: {}
})
getUserByUid(apiKey,'mail,details',function(err,user){
if(user){
try{
editSession({
auth: sessionKey
},{
mail: user.mail,
details: s.parseJSON(user.details),
lang: s.getLanguageFile(user.details.lang)
})
}catch(er){
console.log('FAILED TO EDIT',er)
}
}
callback(err,s.api[params.auth])
})
}else{
getUserBySessionKey(params,function(err,user){
if(user){
isSessionKey = true
createSession(apiKey,{
details: JSON.parse(user.details),
permissions: {}
})
callback(err,user,isSessionKey)
}
})
}
})
}
var createSession = function(user,additionalData){
if(user){
var generatedId
if(!additionalData)additionalData = {}
if(!user.ip)user.ip = '0.0.0.0'
if(!user.auth && !user.code){
generatedId = s.gid(20)
}else{
generatedId = user.auth || user.code
}
user.details = s.parseJSON(user.details)
user.permissions = {}
s.api[generatedId] = Object.assign(user,additionalData)
return generatedId
}
}
var editSession = function(user,additionalData){
if(user){
if(!additionalData)additionalData = {}
Object.keys(additionalData).forEach(function(value,key){
s.api[user.auth][key] = value
})
}
}
var failHttpAuthentication = function(res,req,message){
if(!message)message = lang['Not Authorized']
res.end(s.prettyPrint({
ok: false,
msg: message
}))
}
var resetActiveSessionTimer = function(activeSession){
if(activeSession){
clearTimeout(activeSession.timeout)
activeSession.timeout = setTimeout(function(){
delete(activeSession)
},1000 * 60 * 5)
}
}
s.auth = function(params,onSuccessComplete,res,req){
if(req){
//express (http server) use of auth function
params.ip=req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
var failed=function(){
if(!req.ret){req.ret={ok:false}}
req.ret.msg=lang['Not Authorized'];
res.end(s.s(req.ret));
params.ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
var onFail = function(message){
failHttpAuthentication(res,req,message)
}
}else{
//socket.io use of auth function
var failed = function(){
var onFail = function(){
//maybe log
}
}
var clearAfterTime=function(){
//remove temp key from memory
clearTimeout(s.api[params.auth].timeout)
s.api[params.auth].timeout=setTimeout(function(){
delete(s.api[params.auth])
},1000*60*5)
}
//check IP address of connecting user
var finish=function(user){
if(s.api[params.auth].ip.indexOf('0.0.0.0')>-1||s.api[params.auth].ip.indexOf(params.ip)>-1){
var onSuccess = function(user){
var activeSession = s.api[params.auth]
if(
activeSession &&
(
activeSession.ip.indexOf('0.0.0.0') > -1 ||
activeSession.ip.indexOf(params.ip) > -1
)
){
if(!user.lang){
var details = s.parseJSON(user.details).lang
user.lang = s.getDefinitonFile(user.details.lang) || s.copySystemDefaultLanguage()
}
cb(user);
onSuccessComplete(user)
}else{
failed();
onFail()
}
}
//check if auth key is user's temporary session key
if(s.group[params.ke]&&s.group[params.ke].users&&s.group[params.ke].users[params.auth]){
s.group[params.ke].users[params.auth].permissions={};
if(!s.group[params.ke].users[params.auth].lang){
s.group[params.ke].users[params.auth].lang = s.copySystemDefaultLanguage()
if(s.group[params.ke] && s.group[params.ke].users && s.group[params.ke].users[params.auth]){
var activeSession = s.group[params.ke].users[params.auth]
activeSession.permissions = {}
if(!activeSession.lang){
activeSession.lang = s.copySystemDefaultLanguage()
}
cb(s.group[params.ke].users[params.auth])
onSuccessComplete(activeSession)
}else{
//check if key is already in memory to save query time
if(s.api[params.auth]&&s.api[params.auth].details){
finish(s.api[params.auth]);
if(s.api[params.auth].timeout){
clearAfterTime()
if(s.api[params.auth] && s.api[params.auth].details){
var activeSession = s.api[params.auth]
onSuccess(activeSession)
if(activeSession.timeout){
resetActiveSessionTimer(activeSession)
}
}else{
//no key in memory, query db to see if key exists
//check if using username and password in plain text or md5
if(params.username&&params.username!==''&&params.password&&params.password!==''){
s.sqlQuery('SELECT * FROM Users WHERE mail=? AND (pass=? OR pass=?)',[params.username,params.password,s.createHash(params.password)],function(err,r){
if(r&&r[0]){
r=r[0];
r.ip='0.0.0.0';
r.auth = s.gid(20);
params.auth = r.auth;
r.details=JSON.parse(r.details);
r.permissions = {};
s.api[r.auth]=r;
clearAfterTime();
finish(r);
if(params.username && params.username !== '' && params.password && params.password !== ''){
loginWithUsernameAndPassword(params,'*',function(err,user){
if(user){
params.auth = user.auth
createSession(user)
resetActiveSessionTimer(s.api[params.auth])
onSuccess(user)
}else{
failed();
onFail()
}
})
}else{
//not using plain login
s.sqlQuery('SELECT * FROM API WHERE code=? AND ke=?',[params.auth,params.ke],function(err,r){
if(r&&r[0]){
r=r[0];
s.api[params.auth]={ip:r.ip,uid:r.uid,ke:r.ke,permissions:JSON.parse(r.details),details:{}};
s.sqlQuery('SELECT mail,details FROM Users WHERE uid=? AND ke=?',[r.uid,r.ke],function(err,rr){
if(rr&&rr[0]){
rr=rr[0];
try{
s.api[params.auth].mail=rr.mail
s.api[params.auth].details=JSON.parse(rr.details)
s.api[params.auth].lang=s.getLanguageFile(s.api[params.auth].details.lang)
}catch(er){}
}
finish(s.api[params.auth]);
loginWithApiKey(params,function(err,user,isSessionKey){
if(isSessionKey)resetActiveSessionTimer(s.api[params.auth])
if(user){
createSession(user,{
auth: params.auth
})
onSuccess(s.api[params.auth])
}else{
s.sqlQuery('SELECT * FROM Users WHERE auth=? AND ke=?',[params.auth,params.ke],function(err,r){
if(r&&r[0]){
r=r[0];
r.ip='0.0.0.0'
s.api[params.auth]=r
s.api[params.auth].details=JSON.parse(r.details)
s.api[params.auth].permissions={}
clearAfterTime()
finish(r)
}else{
failed();
}
})
onFail()
}
})
}
@ -121,8 +196,10 @@ module.exports = function(s,config,lang){
var adminUsersSelected = null
try{
var success = function(){
var chosenConfig = config
if(req && res){
res.setHeader('Content-Type', 'application/json');
chosenConfig = s.getConfigWithBranding(req.hostname)
res.setHeader('Content-Type', 'application/json')
var ip = req.headers['cf-connecting-ip']||req.headers["CF-Connecting-IP"]||req.headers["'x-forwarded-for"]||req.connection.remoteAddress;
var resp = {
ok: userFound,
@ -141,9 +218,9 @@ module.exports = function(s,config,lang){
}
callback({
ip : ip,
$user:userSelected,
users:adminUsersSelected,
config:config,
$user: userSelected,
users: adminUsersSelected,
config: chosenConfig,
lang:lang
})
}
@ -199,4 +276,18 @@ module.exports = function(s,config,lang){
return false
}
}
s.basicOrApiAuthentication = function(username,password,callback){
var splitUsername = username.split('@')
if(splitUsername[1] && splitUsername[1].toLowerCase().indexOf('shinobi') > -1){
getApiKey({
auth: splitUsername[0],
ke: password
},'ke,uid',callback)
}else{
loginWithUsernameAndPassword({
username: username,
password: password
},'ke,uid',callback)
}
}
}

View file

@ -1,10 +1,12 @@
var moment = require('moment');
var crypto = require('crypto');
var exec = require('child_process').exec;
var fs = require('fs')
var moment = require('moment')
var crypto = require('crypto')
var exec = require('child_process').exec
var spawn = require('child_process').spawn;
var events = require('events');
var http = require('http');
var https = require('https');
var events = require('events')
var http = require('http')
var https = require('https')
const async = require("async")
module.exports = function(s,config){
//kill any ffmpeg running
s.ffmpegKill=function(){
@ -75,7 +77,12 @@ module.exports = function(s,config){
break;
}
//load camera controller vars
s.nameToTime=function(x){x=x.split('.')[0].split('T'),x[1]=x[1].replace(/-/g,':');x=x.join(' ');return x;}
s.nameToTime=function(x){
x = x.split('.')[0].split('T')
if(x[1])x[1] = x[1].replace(/-/g,':')
x = x.join(' ')
return x
}
s.ratio=function(width,height,ratio){ratio = width / height;return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';}
s.randomNumber=function(x){
if(!x){x=10};
@ -206,7 +213,7 @@ module.exports = function(s,config){
}
return url
}
s.file=function(x,e){
s.file = function(x,e,callback){
if(!e){e={}};
switch(x){
case'size':
@ -214,19 +221,25 @@ module.exports = function(s,config){
break;
case'delete':
if(!e){return false;}
return exec('rm -f '+e,{detached: true});
return exec('rm -f '+e,{detached: true},function(err){
if(callback)callback(err)
})
break;
case'deleteFolder':
if(!e){return false;}
return exec('rm -rf '+e,{detached: true});
exec('rm -rf '+e,{detached: true},function(err){
if(callback)callback(err)
})
break;
case'deleteFiles':
if(!e.age_type){e.age_type='min'};if(!e.age){e.age='1'};
exec('find '+e.path+' -type f -c'+e.age_type+' +'+e.age+' -exec rm -f {} +',{detached: true});
exec('find '+e.path+' -type f -c'+e.age_type+' +'+e.age+' -exec rm -f {} +',{detached: true},function(err){
if(callback)callback(err)
})
break;
}
}
s.createTimeout = function(timeoutVar,timeoutLength,defaultLength,multiplier,callback){
s.createTimeout = function(timeoutVar,parentVar,timeoutLength,defaultLength,multiplier,callback){
var theTimeout
if(!multiplier)multiplier = 1000 * 60
if(!timeoutLength || timeoutLength === ''){
@ -234,12 +247,43 @@ module.exports = function(s,config){
}else{
theTimeout = parseFloat(timeoutLength) * multiplier
}
clearTimeout(timeoutVar)
timeoutVar = setTimeout(function(){
clearTimeout(timeoutVar)
delete(timeoutVar)
clearTimeout(parentVar[timeoutVar])
parentVar[timeoutVar] = setTimeout(function(){
clearTimeout(parentVar[timeoutVar])
delete(parentVar[timeoutVar])
if(callback)callback()
},theTimeout)
return parentVar[timeoutVar]
}
s.handleFolderError = function(err){
if(err){
switch(err.code){
case'EEXIST':
break;
default:
console.log(err)
break;
}
}
}
s.isCorrectFilenameSyntax = function(string){
return RegExp('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]').test(string)
}
var readFile = async.queue(function(filename, callback) {
fs.readFile(filename,"utf-8",callback)
}, 4);
s.readFile = function(filename, callback){
return readFile.push(filename, callback)
}
var fileStats = async.queue(function(filename, callback) {
fs.stat(filename,callback)
}, 4);
s.fileStats = function(filename, callback){
return fileStats.push(filename, callback)
}
s.kilobyteToMegabyte = function(kb,places){
if(!places)places = 2
return (kb/1000000).toFixed(places)
}
Object.defineProperty(Array.prototype, 'chunk', {
value: function(chunkSize){

20
libs/branding.js Normal file
View file

@ -0,0 +1,20 @@
module.exports = function(s,config,lang,app,io){
if(config.showPoweredByShinobi === undefined){config.showPoweredByShinobi=true}
if(config.poweredByShinobi === undefined){config.poweredByShinobi='Powered by Shinobi.Systems'}
if(config.poweredByShinobiClass === undefined){config.poweredByShinobiClass='margin:15px 0 0 0;text-align:center;color:#777;font-family: sans-serif;text-transform: uppercase;letter-spacing: 3;font-size: 8pt;'}
if(config.webPageTitle === undefined){config.webPageTitle='Shinobi'}
if(config.showLoginCardHeader === undefined){config.showLoginCardHeader=true}
if(config.webFavicon === undefined){config.webFavicon='libs/img/icon/favicon.ico'}
if(config.logoLocation76x76 === undefined){config.logoLocation76x76='libs/img/icon/apple-touch-icon-76x76.png'}
if(config.logoLocation76x76Link === undefined){config.logoLocation76x76Link='https://shinobi.video'}
if(config.logoLocation76x76Style === undefined){config.logoLocation76x76Style='border-radius:50%'}
if(config.showLoginSelector === undefined){config.showLoginSelector=true}
s.getConfigWithBranding = function(domain){
var configCopy = Object.assign({},config)
if(config.brandingConfig && config.brandingConfig[domain]){
return Object.assign(configCopy,config.brandingConfig[domain])
}
return config
}
}

View file

@ -13,7 +13,10 @@ module.exports = function(s,config,lang,app,io){
console.log(lang.Shinobi+' - CHILD NODE PORT : '+config.childNodes.port);
});
s.debugLog('childNodeWebsocket.attach(childNodeServer)')
childNodeWebsocket.attach(childNodeServer);
childNodeWebsocket.attach(childNodeServer,{
path:'/socket.io',
transports: ['websocket']
});
//send data to child node function (experimental)
s.cx = function(z,y,x){
if(!z.mid && !z.d){
@ -27,10 +30,12 @@ module.exports = function(s,config,lang,app,io){
//child Node Websocket
childNodeWebsocket.on('connection', function (cn) {
//functions for dispersing work to child servers;
var ipAddress
cn.on('c',function(d){
if(config.childNodes.key.indexOf(d.socketKey) > -1){
if(!cn.shinobi_child&&d.f=='init'){
cn.ip = cn.request.connection.remoteAddress.replace('::ffff:','')+':'+d.port
ipAddress = cn.request.connection.remoteAddress.replace('::ffff:','')+':'+d.port
cn.ip = ipAddress
cn.shinobi_child = 1
tx = function(z){
cn.emit('c',z)
@ -38,8 +43,10 @@ module.exports = function(s,config,lang,app,io){
if(!s.childNodes[cn.ip]){
s.childNodes[cn.ip] = {}
};
s.childNodes[cn.ip].dead = false
s.childNodes[cn.ip].cnid = cn.id
s.childNodes[cn.ip].cpu = 0
s.childNodes[cn.ip].ip = ipAddress
s.childNodes[cn.ip].activeCameras = {}
d.availableHWAccels.forEach(function(accel){
if(config.availableHWAccels.indexOf(accel) === -1)config.availableHWAccels.push(accel)
@ -52,13 +59,16 @@ module.exports = function(s,config,lang,app,io){
}else{
switch(d.f){
case'cpu':
s.childNodes[cn.ip].cpu = d.cpu;
s.childNodes[ipAddress].cpu = d.cpu;
break;
case'sql':
s.sqlQuery(d.query,d.values,function(err,rows){
cn.emit('c',{f:'sqlCallback',rows:rows,err:err,callbackId:d.callbackId});
});
break;
case'clearCameraFromActiveList':
if(s.childNodes[ipAddress])delete(s.childNodes[ipAddress].activeCameras[d.ke + d.id])
break;
case'camera':
s.camera(d.mode,d.data)
break;
@ -69,24 +79,54 @@ module.exports = function(s,config,lang,app,io){
if(!d.mon || !d.data)return console.log('LOG DROPPED',d.mon,d.data);
s.userLog(d.mon,d.data)
break;
case'created_file_chunk':
if(!s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename]){
d.dir = s.getVideoDirectory(s.group[d.ke].mon_conf[d.mid])
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(d.dir+d.filename)
case'open_timelapse_file_transfer':
var location = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/`
if(!fs.existsSync(location)){
fs.mkdirSync(location)
}
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
break;
case'created_file':
if(!s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename]){
case'created_timelapse_file_chunk':
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
var dir = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/`
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(dir+d.filename)
}
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
break;
case'created_timelapse_file':
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
return console.log('FILE NOT EXIST')
}
s.group[d.ke].mon[d.mid].childNodeStreamWriters[d.filename].end();
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end()
tx({
f: 'deleteTimelapseFrame',
file: d.filename,
currentDate: d.currentDate,
d: d.d, //monitor config
ke: d.ke,
mid: d.mid
})
s.insertTimelapseFrameDatabaseRow({
ke: d.ke
},d.queryInfo)
break;
case'created_file_chunk':
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
d.dir = s.getVideoDirectory(s.group[d.ke].rawMonitorConfigurations[d.mid])
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(d.dir+d.filename)
}
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].write(d.chunk)
break;
case'created_file':
if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){
return console.log('FILE NOT EXIST')
}
s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end();
tx({
f:'delete',
file:d.filename,
ke:d.ke,
mid:d.mid
});
})
s.txWithSubPermissions({
f:'video_build_success',
hrefNoAuth:'/videos/'+d.ke+'/'+d.mid+'/'+d.filename,
@ -115,28 +155,39 @@ module.exports = function(s,config,lang,app,io){
s.purgeDiskForGroup(d)
//send new diskUsage values
s.setDiskUsedForGroup(d,insert.filesizeMB)
clearTimeout(s.group[d.ke].mon[d.mid].recordingChecker)
clearTimeout(s.group[d.ke].mon[d.mid].streamChecker)
clearTimeout(s.group[d.ke].activeMonitors[d.mid].recordingChecker)
clearTimeout(s.group[d.ke].activeMonitors[d.mid].streamChecker)
break;
}
}
}
})
cn.on('disconnect',function(){
console.log('childNodeWebsocket.disconnect')
if(s.childNodes[cn.ip]){
var activeCameraKeys = Object.keys(s.childNodes[cn.ip].activeCameras)
activeCameraKeys.forEach(function(key){
var monitor = s.childNodes[cn.ip].activeCameras[key]
s.camera('stop',s.cleanMonitorObject(monitor))
delete(s.group[monitor.ke].mon[monitor.mid].childNode)
delete(s.group[monitor.ke].mon[monitor.mid].childNodeId)
setTimeout(function(){
s.camera(monitor.mode,s.cleanMonitorObject(monitor))
},1300)
})
delete(s.childNodes[cn.ip]);
console.log('childNodeWebsocket.disconnect',ipAddress)
if(s.childNodes[ipAddress]){
var monitors = Object.values(s.childNodes[ipAddress].activeCameras)
if(monitors && monitors[0]){
var loadCompleted = 0
var loadMonitor = function(monitor){
setTimeout(function(){
var mode = monitor.mode + ''
var cleanMonitor = s.cleanMonitorObject(monitor)
s.camera('stop',Object.assign(cleanMonitor,{}))
delete(s.group[monitor.ke].activeMonitors[monitor.mid].childNode)
delete(s.group[monitor.ke].activeMonitors[monitor.mid].childNodeId)
setTimeout(function(){
s.camera(mode,cleanMonitor)
++loadCompleted
if(monitors[loadCompleted]){
loadMonitor(monitors[loadCompleted])
}
},1000)
},2000)
}
loadMonitor(monitors[loadCompleted])
}
s.childNodes[ipAddress].activeCameras = {}
s.childNodes[ipAddress].dead = true
}
})
})
@ -144,7 +195,9 @@ module.exports = function(s,config,lang,app,io){
//setup Child for childNodes
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
s.connected = false;
childIO = require('socket.io-client')('ws://'+config.childNodes.host);
childIO = require('socket.io-client')('ws://'+config.childNodes.host,{
transports: ['websocket']
});
s.cx = function(x){x.socketKey = config.childNodes.key;childIO.emit('c',x)}
s.tx = function(x,y){s.cx({f:'s.tx',data:x,to:y})}
s.userLog = function(x,y){s.cx({f:'s.userLog',mon:x,data:y})}
@ -162,9 +215,9 @@ module.exports = function(s,config,lang,app,io){
}
setInterval(function(){
s.cpuUsage(function(cpu){
io.emit('c',{f:'cpu',cpu:parseFloat(cpu)});
s.cx({f:'cpu',cpu:parseFloat(cpu)})
})
},2000);
},2000)
childIO.on('connect', function(d){
console.log('CHILD CONNECTION SUCCESS')
s.cx({
@ -188,17 +241,22 @@ module.exports = function(s,config,lang,app,io){
break;
case'kill':
s.initiateMonitorObject(d.d);
s.cameraDestroy(s.group[d.d.ke].mon[d.d.id].spawn,d.d)
s.cameraDestroy(s.group[d.d.ke].activeMonitors[d.d.id].spawn,d.d)
var childNodeIp = s.group[d.d.ke].activeMonitors[d.d.id]
break;
case'sync':
s.initiateMonitorObject(d.sync);
Object.keys(d.sync).forEach(function(v){
s.group[d.sync.ke].mon[d.sync.mid][v]=d.sync[v];
s.group[d.sync.ke].activeMonitors[d.sync.mid][v]=d.sync[v];
});
break;
case'delete'://delete video
s.file('delete',s.dir.videos+d.ke+'/'+d.mid+'/'+d.file)
break;
case'deleteTimelapseFrame'://delete video
var filePath = s.getTimelapseFrameDirectory(d.d) + `${d.currentDate}/` + d.file
s.file('delete',filePath)
break;
case'insertCompleted'://close video
s.insertCompletedVideo(d.d,d.k)
break;
@ -212,6 +270,15 @@ module.exports = function(s,config,lang,app,io){
})
childIO.on('disconnect',function(d){
s.connected = false;
var groupKeys = Object.keys(s.group)
groupKeys.forEach(function(groupKey){
var activeMonitorKeys = Object.keys(s.group[groupKey].activeMonitors)
activeMonitorKeys.forEach(function(monitorKey){
var activeMonitor = s.group[groupKey].activeMonitors[monitorKey]
if(activeMonitor && activeMonitor.spawn && activeMonitor.spawn.close)activeMonitor.spawn.close()
if(activeMonitor && activeMonitor.spawn && activeMonitor.spawn.kill)activeMonitor.spawn.kill()
})
})
})
}
}

View file

@ -6,12 +6,12 @@ module.exports = function(s){
}
var config = require(s.location.config);
if(!config.productType){
config.productType='CE'
config.productType = 'CE'
}
//config defaults
if(config.cpuUsageMarker === undefined){config.cpuUsageMarker='%Cpu'}
if(config.customCpuCommand === undefined){config.customCpuCommand=null}
if(config.autoDropCache === undefined){config.autoDropCache=true}
if(config.autoDropCache === undefined){config.autoDropCache=false}
if(config.doSnapshot === undefined){config.doSnapshot=true}
if(config.restart === undefined){config.restart={}}
if(config.systemLog === undefined){config.systemLog=true}
@ -27,7 +27,8 @@ module.exports = function(s){
if(config.cron.deleteOverMaxOffset === undefined)config.cron.deleteOverMaxOffset=0.9;
if(config.cron.deleteLogs === undefined)config.cron.deleteLogs=true;
if(config.cron.deleteEvents === undefined)config.cron.deleteEvents=true;
if(config.cron.deleteFileBins === undefined)config.cron.deleteFileBins=true;
if(config.cron.deleteFileBinsOverMax === undefined)config.cron.deleteFileBins=true;
if(config.deleteFileBins === undefined)config.deleteFileBinsOverMax=true;
if(config.cron.interval === undefined)config.cron.interval=1;
if(config.databaseType === undefined){config.databaseType='mysql'}
if(config.pluginKeys === undefined)config.pluginKeys={};
@ -37,7 +38,7 @@ module.exports = function(s){
if(config.pipeAddition === undefined){config.pipeAddition=10}else{config.pipeAddition=parseInt(config.pipeAddition)}
if(config.hideCloudSaveUrls === undefined){config.hideCloudSaveUrls = true}
if(config.insertOrphans === undefined){config.insertOrphans = true}
if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 20}
if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 2}
if(config.detectorMergePamRegionTriggers === undefined){config.detectorMergePamRegionTriggers = false}
//Child Nodes
if(config.childNodes === undefined)config.childNodes = {};

View file

@ -1,6 +1,28 @@
var fs = require('fs')
var express = require('express')
module.exports = function(s,config,lang,app,io){
function mergeDeep(...objects) {
const isObject = obj => obj && typeof obj === 'object';
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach(key => {
const pVal = prev[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
}
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
return prev;
}, {});
}
s.customAutoLoadModules = {}
s.customAutoLoadTree = {
pages: [],
@ -14,7 +36,7 @@ module.exports = function(s,config,lang,app,io){
superLibsJs: [],
superLibsCss: []
}
var folderPath = __dirname + '/customAutoLoad'
var folderPath = s.mainDirectory + '/libs/customAutoLoad'
var search = function(searchFor,searchIn){return searchIn.indexOf(searchFor) > -1}
fs.readdir(folderPath,function(err,folderContents){
if(!err && folderContents){
@ -117,6 +139,24 @@ module.exports = function(s,config,lang,app,io){
})
})
break;
case'definitions':
var definitionsFolder = s.checkCorrectPathEnding(customModulePath) + 'definitions/'
fs.readdir(definitionsFolder,function(err,files){
if(err)return console.log(err);
files.forEach(function(filename){
var fileData = require(definitionsFolder + filename)
var rule = filename.replace('.json','').replace('.js','')
if(config.language === rule){
s.definitions = mergeDeep(s.definitions,fileData)
}
if(s.loadedDefinitons[rule]){
s.loadedDefinitons[rule] = mergeDeep(s.loadedDefinitons[rule],fileData)
}else{
s.loadedDefinitons[rule] = mergeDeep(s.copySystemDefaultDefinitions(),fileData)
}
})
})
break;
}
})
})

44
libs/definitions.js Normal file
View file

@ -0,0 +1,44 @@
var fs = require('fs')
var express = require('express')
module.exports = function(s,config,lang,app,io){
s.location.definitions = s.mainDirectory+'/definitions'
try{
var definitions = require(s.location.definitions+'/'+config.language+'.js')(s,config,lang)
}catch(er){
console.error(er)
console.log('There was an error loading your definition file.')
try{
var definitions = require(s.location.definitions+'/en_CA.js')(s,config,lang)
}catch(er){
console.error(er)
console.log('There was an error loading your definition file.')
var definitions = require(s.location.definitions+'/en_CA.json');
}
}
//load defintions dynamically
s.definitions = definitions
s.copySystemDefaultDefinitions = function(){
//en_CA
return Object.assign(s.definitions,{})
}
s.loadedDefinitons={}
s.loadedDefinitons[config.language] = s.copySystemDefaultDefinitions()
s.getDefinitonFile = function(rule){
if(rule && rule !== ''){
var file = s.loadedDefinitons[rule]
if(!file){
try{
s.loadedDefinitons[rule] = require(s.location.definitions+'/'+rule+'.js')(s,config,lang)
s.loadedDefinitons[rule] = Object.assign(s.copySystemDefaultDefinitions(),s.loadedDefinitons[rule])
file = s.loadedDefinitons[rule]
}catch(err){
file = s.copySystemDefaultDefinitions()
}
}
}else{
file = s.copySystemDefaultDefinitions()
}
return file
}
return definitions
}

View file

@ -13,9 +13,9 @@ module.exports = function(s,config){
globalSensitivity,
globalColorThreshold,
fullFrame = false
if(s.group[e.ke].mon_conf[e.id].details.detector_scale_x===''||s.group[e.ke].mon_conf[e.id].details.detector_scale_y===''){
width = s.group[e.ke].mon_conf[e.id].details.detector_scale_x;
height = s.group[e.ke].mon_conf[e.id].details.detector_scale_y;
if(s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x===''||s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y===''){
width = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x;
height = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y;
}else{
width = e.width
height = e.height
@ -35,9 +35,9 @@ module.exports = function(s,config){
var regionJson
try{
regionJson = JSON.parse(s.group[e.ke].mon_conf[e.id].details.cords)
regionJson = JSON.parse(s.group[e.ke].rawMonitorConfigurations[e.id].details.cords)
}catch(err){
regionJson = s.group[e.ke].mon_conf[e.id].details.cords
regionJson = s.group[e.ke].rawMonitorConfigurations[e.id].details.cords
}
if(Object.keys(regionJson).length === 0 || e.details.detector_frame === '1'){
@ -64,8 +64,8 @@ module.exports = function(s,config){
if(e.details.detector_show_matrix==='1'){
pamDiffOptions.response = 'bounds'
}
s.group[e.ke].mon[e.id].pamDiff = new PamDiff(pamDiffOptions);
s.group[e.ke].mon[e.id].p2p = new P2P()
s.group[e.ke].activeMonitors[e.id].pamDiff = new PamDiff(pamDiffOptions);
s.group[e.ke].activeMonitors[e.id].p2p = new P2P()
var regionArray = Object.values(regionJson)
if(config.detectorMergePamRegionTriggers === true){
// merge pam triggers for performance boost
@ -116,12 +116,12 @@ module.exports = function(s,config){
}
}
if(e.details.detector_noise_filter==='1'){
if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
var filteredCount = 0
var filteredCountSuccess = 0
data.trigger.forEach(function(trigger){
@ -135,7 +135,7 @@ module.exports = function(s,config){
})
})
}else{
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
buildTriggerEvent(s.mergePamTriggers(data))
})
}
@ -170,12 +170,12 @@ module.exports = function(s,config){
})
}
if(e.details.detector_noise_filter==='1'){
if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
data.trigger.forEach(function(trigger){
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
s.createMatrixFromPamTrigger(trigger)
@ -184,7 +184,7 @@ module.exports = function(s,config){
})
})
}else{
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
data.trigger.forEach(function(trigger){
s.createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
@ -210,7 +210,14 @@ module.exports = function(s,config){
if(!region)return false;
region.polygon = [];
region.points.forEach(function(points){
region.polygon.push({x:parseFloat(points[0]),y:parseFloat(points[1])})
var x = parseFloat(points[0]);
var y = parseFloat(points[1]);
if(x < 0)x = 0;
if(y < 0)y = 0;
region.polygon.push({
x: x,
y: y
})
})
if(region.sensitivity===''){
region.sensitivity = globalSensitivity

View file

@ -1,35 +1,8 @@
var fs = require('fs')
var execSync = require('child_process').execSync
module.exports = function(s,config,lang,app,io){
if(config.dropInEventServer === true){
if(config.dropInEventForceSaveEvent === undefined)config.dropInEventForceSaveEvent = true
if(config.dropInEventDeleteFileAfterTrigger === undefined)config.dropInEventDeleteFileAfterTrigger = true
var authenticateUser = function(username,password,callback){
var splitUsername = username.split('@')
if(splitUsername[1] !== 'Shinobi' && splitUsername[1] !== 'shinobi'){
s.sqlQuery('SELECT ke,uid FROM Users WHERE mail=? AND (pass=? OR pass=?)',[
username,
password,
s.createHash(password)
],function(err,r){
var user
if(r && r[0]){
user = r[0]
}
callback(err,user)
})
}else{
s.sqlQuery('SELECT ke,uid FROM API WHERE code=? AND ke=?',[
splitUsername[0], //code
password //ke
],function(err,r){
var apiKey
if(r && r[0]){
apiKey = r[0]
}
callback(err,apiKey)
})
}
}
var beforeMonitorsLoadedOnStartup = function(){
if(!config.dropInEventsDir){
config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
@ -50,74 +23,131 @@ module.exports = function(s,config,lang,app,io){
var onMonitorStop = function(monitorConfig){
var ke = monitorConfig.ke
var mid = monitorConfig.mid
if(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher){
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher.close()
delete(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher)
if(s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher){
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher.close()
delete(s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher)
var monitorEventDropDir = getDropInEventDir(monitorConfig)
s.file('deleteFolder',monitorEventDropDir + '*')
}
var monitorEventDropDir = getDropInEventDir(monitorConfig)
if(fs.existsSync(monitorEventDropDir))execSync('rm -rf ' + monitorEventDropDir)
}
var createDropInEventDirectory = function(e,callback){
var directory = s.dir.dropInEvents + e.ke + '/'
fs.mkdir(directory,function(err){
s.handleFolderError(err)
directory = s.dir.dropInEvents + e.ke + '/' + (e.id || e.mid) + '/'
fs.mkdir(directory,function(err){
s.handleFolderError(err)
callback(err,directory)
})
})
}
var onMonitorInit = function(monitorConfig){
onMonitorStop(monitorConfig)
var ke = monitorConfig.ke
var mid = monitorConfig.mid
var monitorEventDropDir = getDropInEventDir(monitorConfig)
var groupEventDropDir = s.dir.dropInEvents + ke
if(!fs.existsSync(groupEventDropDir)){
fs.mkdirSync(groupEventDropDir)
}
var monitorEventDropDir = groupEventDropDir + '/' + mid + '/'
if(!fs.existsSync(monitorEventDropDir)){
fs.mkdirSync(monitorEventDropDir)
}
var fileQueue = {}
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventFileQueue = fileQueue
var eventTrigger = function(eventType,filename){
var filePath = monitorEventDropDir + filename
if(filename.indexOf('.jpg') > -1 || filename.indexOf('.jpeg') > -1){
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
fs.unlink(snapPath,function(err){
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: "dropInEvent"
createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){
var monitorEventDropDir = getDropInEventDir(monitorConfig)
var fileQueue = {}
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventFileQueue = fileQueue
var search = function(searchIn,searchFor){
return searchIn.indexOf(searchFor) > -1
}
var processFile = function(filename){
var filePath = monitorEventDropDir + filename
if(search(filename,'.jpg') || search(filename,'.jpeg')){
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
fs.unlink(snapPath,function(err){
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: "ftpServer"
},
},config.dropInEventForceSaveEvent)
})
}else{
var reason = "ftpServer"
if(search(filename,'.mp4')){
fs.stat(filePath,function(err,stats){
var startTime = stats.ctime
var endTime = stats.mtime
var shinobiFilename = s.formattedTime(startTime) + '.mp4'
var recordingPath = s.getVideoDirectory(monitorConfig) + shinobiFilename
var writeStream = fs.createWriteStream(recordingPath)
fs.createReadStream(filePath).pipe(writeStream)
writeStream.on('finish', () => {
s.insertCompletedVideo(s.group[monitorConfig.ke].rawMonitorConfigurations[monitorConfig.mid],{
file : shinobiFilename
},function(){
})
})
})
}
var completeAction = function(){
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: reason
}
},config.dropInEventForceSaveEvent)
}
if(search(filename,'.txt')){
fs.readFile(filePath,{encoding: 'utf-8'},function(err,data){
if(data){
reason = data.split('\n')[0] || filename
}else if(filename){
reason = filename
}
completeAction()
})
}else{
completeAction()
}
}
if(config.dropInEventDeleteFileAfterTrigger){
setTimeout(function(){
fs.unlink(filePath,function(err){
})
},1000 * 60 * 5)
}
}
var eventTrigger = function(eventType,filename,stats){
if(stats.isDirectory()){
fs.readdir(monitorEventDropDir + filename,function(err,files){
if(files){
files.forEach(function(filename){
processFile(filename)
})
}else if(err){
console.log(err)
}
})
})
}else{
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: "ftpServer"
}else{
processFile(filename)
}
}
var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){
fs.stat(monitorEventDropDir + filename,function(err,stats){
if(!err){
clearTimeout(fileQueue[filename])
fileQueue[filename] = setTimeout(function(){
eventTrigger(eventType,filename,stats)
},1750)
}
})
}
if(config.dropInEventDeleteFileAfterTrigger){
setTimeout(function(){
fs.unlink(filePath,function(err){
})
},1000 * 60 * 5)
}
}
var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){
if(fs.existsSync(monitorEventDropDir + filename)){
clearTimeout(fileQueue[filename])
fileQueue[filename] = setTimeout(function(){
eventTrigger(eventType,filename)
},1200)
}
})
s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].dropInEventWatcher = directoryWatch
})
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher = directoryWatch
}
// FTP Server
if(config.ftpServer === true){
@ -127,13 +157,12 @@ module.exports = function(s,config,lang,app,io){
const FtpSrv = require('ftp-srv')
const ftpServer = new FtpSrv({
url: config.ftpServerUrl,
// log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){}
})
ftpServer.on('login', (data, resolve, reject) => {
var username = data.username
var password = data.password
authenticateUser(username,password,function(err,user){
s.basicOrApiAuthentication(username,password,function(err,user){
if(user){
resolve({root: s.dir.dropInEvents + user.ke})
}else{
@ -141,7 +170,9 @@ module.exports = function(s,config,lang,app,io){
}
})
})
ftpServer.on('client-error', ({connection, context, error}) => {
console.log('error')
})
ftpServer.listen().then(() => {
s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`)
}).catch(function(err){
@ -156,13 +187,15 @@ module.exports = function(s,config,lang,app,io){
// SMTP Server
// allow starting SMTP server without dropInEventServer
if(config.smtpServer === true){
if(config.smtpServerHideStartTls === undefined)config.smtpServerHideStartTls = null
var SMTPServer = require("smtp-server").SMTPServer;
if(!config.smtpServerPort && (config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl)){config.smtpServerPort = 465}else if(!config.smtpServerPort){config.smtpServerPort = 25}
var smtpOptions = {
hideSTARTTLS: config.smtpServerHideStartTls,
onAuth(auth, session, callback) {
var username = auth.username
var password = auth.password
authenticateUser(username,password,function(err,user){
s.basicOrApiAuthentication(username,password,function(err,user){
if(user){
callback(null, {user: user.ke})
}else{
@ -174,21 +207,61 @@ module.exports = function(s,config,lang,app,io){
var split = address.address.split('@')
var monitorId = split[0]
var ke = session.user
if(s.group[ke].mon_conf[monitorId] && s.group[ke].mon[monitorId].isStarted === true){
s.triggerEvent({
id: monitorId,
ke: ke,
details: {
confidence: 100,
name: address.address,
plug: "dropInEvent",
reason: "smtpServer"
}
})
if(s.group[ke].rawMonitorConfigurations[monitorId] && s.group[ke].activeMonitors[monitorId].isStarted === true){
session.monitorId = monitorId
}else{
return callback(new Error(lang['No Monitor Exists with this ID.']))
}
callback()
},
onData(stream, session, callback) {
if(session.monitorId){
var ke = session.user
var monitorId = session.monitorId
var reasonTag = 'smtpServer'
var text = ''
stream.on('data',function(data){
text += data.toString()
}) // print message to console
stream.on("end", function(){
var contentPart = text.split('--PartBoundary12345678')
contentPart.forEach(function(part){
var parsed = {}
var lines = part.split(/\r?\n/)
lines.forEach(function(line,n){
var pieces = line.split(':')
if(pieces[1]){
var nextLine = lines[n + 1]
var keyName = pieces[0].trim().toLowerCase()
pieces.shift()
var parsedValue = pieces.join(':')
parsed[keyName] = parsedValue
}
})
if(parsed['content-type'] && parsed['content-type'].indexOf('image/jpeg') > -1){
// console.log(lines)
}
if(parsed['alarm event']){
reasonTag = parsed['alarm event']
}else if(parsed.subject){
reasonTag = parsed.subject
}
})
s.triggerEvent({
id: monitorId,
ke: ke,
details: {
confidence: 100,
name: 'smtpServer',
plug: "dropInEvent",
reason: reasonTag
}
},config.dropInEventForceSaveEvent)
callback()
})
}else{
callback()
}
}
}
if(config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl && config.ssl.cert && config.ssl.key){

View file

@ -41,7 +41,7 @@ module.exports = function(s,config,lang){
extender(x,d)
})
}
s.triggerEvent = function(d){
s.triggerEvent = function(d,forceSave){
var filter = {
halt : false,
addToMotionCounter : true,
@ -56,11 +56,11 @@ module.exports = function(s,config,lang){
extender(d,filter)
})
var detailString = JSON.stringify(d.details);
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]){
return s.systemLog(lang['No Monitor Found, Ignoring Request'])
}
d.mon=s.group[d.ke].mon_conf[d.id];
var currentConfig = s.group[d.ke].mon[d.id].details
d.mon=s.group[d.ke].rawMonitorConfigurations[d.id];
var currentConfig = s.group[d.ke].activeMonitors[d.id].details
var hasMatrices = (d.details.matrices && d.details.matrices.length > 0)
//read filters
if(
@ -185,15 +185,16 @@ module.exports = function(s,config,lang){
d.details.matrices = reviewedMatrix
}
}
var eventTime = new Date()
//motion counter
if(filter.addToMotionCounter && filter.record){
if(!s.group[d.ke].mon[d.id].detector_motion_count){
s.group[d.ke].mon[d.id].detector_motion_count=0
if(!s.group[d.ke].activeMonitors[d.id].detector_motion_count){
s.group[d.ke].activeMonitors[d.id].detector_motion_count=0
}
s.group[d.ke].mon[d.id].detector_motion_count+=1
s.group[d.ke].activeMonitors[d.id].detector_motion_count+=1
}
if(filter.useLock){
if(s.group[d.ke].mon[d.id].motion_lock){
if(s.group[d.ke].activeMonitors[d.id].motion_lock){
return
}
var detector_lock_timeout
@ -201,10 +202,10 @@ module.exports = function(s,config,lang){
detector_lock_timeout = 2000
}
detector_lock_timeout = parseFloat(currentConfig.detector_lock_timeout);
if(!s.group[d.ke].mon[d.id].detector_lock_timeout){
s.group[d.ke].mon[d.id].detector_lock_timeout=setTimeout(function(){
clearTimeout(s.group[d.ke].mon[d.id].detector_lock_timeout)
delete(s.group[d.ke].mon[d.id].detector_lock_timeout)
if(!s.group[d.ke].activeMonitors[d.id].detector_lock_timeout){
s.group[d.ke].activeMonitors[d.id].detector_lock_timeout=setTimeout(function(){
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
delete(s.group[d.ke].activeMonitors[d.id].detector_lock_timeout)
},detector_lock_timeout)
}else{
return
@ -212,7 +213,7 @@ module.exports = function(s,config,lang){
}
// check if object should be in region
if(hasMatrices && currentConfig.detector_obj_region === '1'){
var regions = s.group[d.ke].mon[d.id].parsedObjects.cords
var regions = s.group[d.ke].activeMonitors[d.id].parsedObjects.cords
var isMatrixInRegions = s.isAtleastOneMatrixInRegion(regions,d.details.matrices)
if(isMatrixInRegions){
s.debugLog('Matrix in region!')
@ -229,14 +230,14 @@ module.exports = function(s,config,lang){
if(d.doObjectDetection === true){
s.ocvTx({
f : 'frame',
mon : s.group[d.ke].mon_conf[d.id].details,
mon : s.group[d.ke].rawMonitorConfigurations[d.id].details,
ke : d.ke,
id : d.id,
time : s.formattedTime(),
frame : s.group[d.ke].mon[d.id].lastJpegDetectorFrame
frame : s.group[d.ke].activeMonitors[d.id].lastJpegDetectorFrame
})
}else{
if(currentConfig.detector_multi_trigger === '1'){
if(currentConfig.det_multi_trig === '1'){
s.getCamerasForMultiTrigger(d.mon).forEach(function(monitor){
if(monitor.mid !== d.id){
s.triggerEvent({
@ -253,8 +254,8 @@ module.exports = function(s,config,lang){
})
}
//save this detection result in SQL, only coords. not image.
if(filter.save && currentConfig.detector_save === '1'){
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,new Date()])
if(forceSave || (filter.save && currentConfig.detector_save === '1')){
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,eventTime])
}
if(currentConfig.detector_notrigger === '1'){
var detector_notrigger_timeout
@ -262,9 +263,9 @@ module.exports = function(s,config,lang){
detector_notrigger_timeout = 10
}
detector_notrigger_timeout = parseFloat(currentConfig.detector_notrigger_timeout)*1000*60;
s.group[d.ke].mon[d.id].detector_notrigger_timeout = detector_notrigger_timeout;
clearInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout)
s.group[d.ke].mon[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout = detector_notrigger_timeout;
clearInterval(s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout)
s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].activeMonitors[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
}
var detector_timeout
if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
@ -273,7 +274,7 @@ module.exports = function(s,config,lang){
detector_timeout = parseFloat(currentConfig.detector_timeout)
}
if(filter.record && d.mon.mode=='start'&&currentConfig.detector_trigger==='1'&&currentConfig.detector_record_method==='sip'){
s.createEventBasedRecording(d)
s.createEventBasedRecording(d,moment(eventTime).subtract(5,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
}else if(filter.record && d.mon.mode!=='stop'&&currentConfig.detector_trigger=='1'&&currentConfig.detector_record_method==='hot'){
if(!d.auth){
d.auth=s.gid();
@ -314,26 +315,32 @@ module.exports = function(s,config,lang){
if(filter.webhook && currentConfig.detector_webhook === '1'){
var detector_webhook_url = addEventDetailsToString(d,currentConfig.detector_webhook_url)
request({url:detector_webhook_url,method:'GET',encoding:null},function(err,data){
var webhookMethod = currentConfig.detector_webhook_method
if(!webhookMethod || webhookMethod === '')webhookMethod = 'GET'
request(detector_webhook_url,{method: webhookMethod,encoding:null},function(err,data){
if(err){
s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}})
}
})
}
if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].mon[d.id].detector_command){
s.createTimeout(s.group[d.ke].mon[d.id].detector_command,currentConfig.detector_command_timeout,10)
if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].activeMonitors[d.id].detector_command){
s.group[d.ke].activeMonitors[d.id].detector_command = s.createTimeout('detector_command',s.group[d.ke].activeMonitors[d.id],currentConfig.detector_command_timeout,10)
var detector_command = addEventDetailsToString(d,currentConfig.detector_command)
exec(detector_command,{detached: true})
if(detector_command === '')return
exec(detector_command,{detached: true},function(err){
if(err)s.debugLog(err)
})
}
}
//show client machines the event
d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
}
s.createEventBasedRecording = function(d){
d.mon = s.group[d.ke].mon_conf[d.id]
var currentConfig = s.group[d.ke].mon[d.id].details
s.createEventBasedRecording = function(d,fileTime){
if(!fileTime)fileTime = s.formattedTime()
d.mon = s.group[d.ke].rawMonitorConfigurations[d.id]
var currentConfig = s.group[d.ke].activeMonitors[d.id].details
if(currentConfig.detector !== '1'){
return
}
@ -343,29 +350,29 @@ module.exports = function(s,config,lang){
}else{
detector_timeout = parseFloat(currentConfig.detector_timeout)
}
if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].mon[d.id].eventBasedRecording.timeout){
clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
s.group[d.ke].mon[d.id].eventBasedRecording.timeout = setTimeout(function(){
s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = true
s.group[d.ke].mon[d.id].eventBasedRecording.process.stdin.setEncoding('utf8')
s.group[d.ke].mon[d.id].eventBasedRecording.process.stdin.write('q')
delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout){
clearTimeout(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout = setTimeout(function(){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = true
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stdin.setEncoding('utf8')
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stdin.write('q')
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
},detector_timeout * 1000 * 60)
}
if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){
s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false;
if(!s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd = false;
var runRecord = function(){
var filename = s.formattedTime()+'.mp4'
var filename = fileTime+'.mp4'
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Started"]})
//-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))
var ffmpegError='';
var error
s.group[d.ke].mon[d.id].eventBasedRecording.process.stderr.on('data',function(data){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.stderr.on('data',function(data){
s.userLog(d,{type:lang["Traditional Recording"],msg:data.toString()})
})
s.group[d.ke].mon[d.id].eventBasedRecording.process.on('close',function(){
if(!s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd){
s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process.on('close',function(){
if(!s.group[d.ke].activeMonitors[d.id].eventBasedRecording.allowEnd){
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Process Exited Prematurely. Restarting."]})
runRecord()
return
@ -375,22 +382,22 @@ module.exports = function(s,config,lang){
})
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Complete"]})
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["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)
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.process)
clearTimeout(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
delete(s.group[d.ke].activeMonitors[d.id].eventBasedRecording.timeout)
clearTimeout(s.group[d.ke].activeMonitors[d.id].recordingChecker)
})
}
runRecord()
}
}
s.closeEventBasedRecording = function(e){
if(s.group[e.ke].mon[e.id].eventBasedRecording.process){
clearTimeout(s.group[e.ke].mon[e.id].eventBasedRecording.timeout)
s.group[e.ke].mon[e.id].eventBasedRecording.allowEnd = true;
s.group[e.ke].mon[e.id].eventBasedRecording.process.kill('SIGTERM');
if(s.group[e.ke].activeMonitors[e.id].eventBasedRecording.process){
clearTimeout(s.group[e.ke].activeMonitors[e.id].eventBasedRecording.timeout)
s.group[e.ke].activeMonitors[e.id].eventBasedRecording.allowEnd = true;
s.group[e.ke].activeMonitors[e.id].eventBasedRecording.process.kill('SIGTERM');
}
// var stackedProcesses = s.group[e.ke].mon[e.id].eventBasedRecording.stackable
// var stackedProcesses = s.group[e.ke].activeMonitors[e.id].eventBasedRecording.stackable
// Object.keys(stackedProcesses).forEach(function(key){
// var item = stackedProcesses[key]
// clearTimeout(item.timeout)

View file

@ -143,4 +143,28 @@ module.exports = function(s,config){
s.onWebSocketDisconnectionExtensions.push(callback)
}
//
s.onGetCpuUsageExtensions = []
s.onGetCpuUsage = function(callback){
s.onGetCpuUsageExtensions.push(callback)
}
//
s.onGetRamUsageExtensions = []
s.onGetRamUsage = function(callback){
s.onGetRamUsageExtensions.push(callback)
}
//
/////// VIDEOS ////////
s.insertCompletedVideoExtensions = []
s.insertCompletedVideoExtender = function(callback){
s.insertCompletedVideoExtensions.push(callback)
}
s.onBeforeInsertCompletedVideoExtensions = []
s.onBeforeInsertCompletedVideo = function(callback){
s.onBeforeInsertCompletedVideoExtensions.push(callback)
}
/////// TIMELAPSE ////////
s.onInsertTimelapseFrameExtensions = []
s.onInsertTimelapseFrame = function(callback){
s.onInsertTimelapseFrameExtensions.push(callback)
}
}

View file

@ -1,7 +1,8 @@
var fs = require('fs');
var spawn = require('child_process').spawn;
var execSync = require('child_process').execSync;
module.exports = function(s,config,onFinish){
module.exports = function(s,config,lang,onFinish){
if(config.ffmpegBinary)config.ffmpegDir = config.ffmpegBinary
var ffmpeg = {}
var downloadingFfmpeg = false;
//check local ffmpeg
@ -98,6 +99,27 @@ module.exports = function(s,config,onFinish){
config.availableHWAccels = availableHWAccels
config.availableHWAccels = ['auto'].concat(config.availableHWAccels)
console.log('Available Hardware Acceleration Methods : ',availableHWAccels.join(', '))
var methods = {
auto: {label:lang['Auto'],value:'auto'},
drm: {label:lang['drm'],value:'drm'},
cuvid: {label:lang['cuvid'],value:'cuvid'},
vaapi: {label:lang['vaapi'],value:'vaapi'},
qsv: {label:lang['qsv'],value:'qsv'},
vdpau: {label:lang['vdpau'],value:'vdpau'},
dxva2: {label:lang['dxva2'],value:'dxva2'},
vdpau: {label:lang['vdpau'],value:'vdpau'},
videotoolbox: {label:lang['videotoolbox'],value:'videotoolbox'}
}
s.listOfHwAccels = []
config.availableHWAccels.forEach(function(availibleMethod){
if(methods[availibleMethod]){
var method = methods[availibleMethod]
s.listOfHwAccels.push({
name: method.label,
value: method.value,
})
}
})
}
callback()
}
@ -471,8 +493,7 @@ module.exports = function(s,config,onFinish){
if(e.details.stream_timestamp_box_color&&e.details.stream_timestamp_box_color!==''){x.stream_timestamp_box_color=e.details.stream_timestamp_box_color}else{x.stream_timestamp_box_color='0x00000000@1'}
//text size
if(e.details.stream_timestamp_font_size&&e.details.stream_timestamp_font_size!==''){x.stream_timestamp_font_size=e.details.stream_timestamp_font_size}else{x.stream_timestamp_font_size='10'}
x.stream_video_filters.push('drawtext=fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size);
x.stream_video_filters.push('drawtext="fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size + '"');
}
//stream - watermark for -vf
if(e.details.stream_watermark&&e.details.stream_watermark=="1"&&e.details.stream_watermark_location&&e.details.stream_watermark_location!==''){
@ -540,6 +561,7 @@ module.exports = function(s,config,onFinish){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.stream)
}
if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps = ''}
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
}
@ -638,8 +660,8 @@ module.exports = function(s,config,onFinish){
x.record_video_filters = []
x.record_string = ''
//record - resolution
if(e.width!==''&&e.height!==''&&!isNaN(e.width)&&!isNaN(e.height)){
x.record_dimensions=' -s '+e.width+'x'+e.height
if(e.record_scale_x!==''&&e.record_scale_y!==''&&e.record_scale_x!=='0'&&e.record_scale_y!=='0'&&!isNaN(e.record_scale_x)&&!isNaN(e.record_scale_y)){
x.record_dimensions=' -s '+e.record_scale_x+'x'+e.record_scale_y
}else{
x.record_dimensions=''
}
@ -647,12 +669,13 @@ module.exports = function(s,config,onFinish){
x.dimensions = e.details.stream_scale_x+'x'+e.details.stream_scale_y;
}
//record - segmenting
x.segment=' -f segment -segment_format_options movflags=faststart+frag_keyframe+empty_moov -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
x.segment = ' -f segment -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
//record - set defaults for extension, video quality
switch(e.ext){
case'mp4':
x.vcodec='libx264';x.acodec='aac';
if(e.details.crf&&e.details.crf!==''){x.vcodec+=' -crf '+e.details.crf}
x.record_video_filters.push(`-segment_format_options movflags=faststart+frag_keyframe+empty_moov`)
break;
case'webm':
x.acodec='libvorbis',x.vcodec='libvpx';
@ -680,8 +703,6 @@ module.exports = function(s,config,onFinish){
if(x.vcodec.indexOf('none')>-1){x.vcodec=''}else{x.vcodec=' -vcodec '+x.vcodec}
//record - frames per second (fps)
if(e.fps&&e.fps!==''&&e.details.vcodec!=='copy'){x.record_fps=' -r '+e.fps}else{x.record_fps=''}
//stream - frames per second (fps)
if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps = ''}
//record - timestamp options for -vf
if(e.details.timestamp&&e.details.timestamp=="1"&&e.details.vcodec!=='copy'){
//font
@ -792,9 +813,11 @@ module.exports = function(s,config,onFinish){
if(sendFramesGlobally)x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe pipe:3'
if(e.details.detector_use_detect_object === '1'){
//for object detection
x.detector_fps_object = '2'
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
if(e.details.detector_scale_x_object&&e.details.detector_scale_x_object!==''&&e.details.detector_scale_y_object&&e.details.detector_scale_y_object!==''){x.dobjratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object}else{x.dobjratio=x.dratio}
x.pipe += ' -r ' + x.detector_fps + x.dobjratio + x.cust_detect
if(e.details.detector_fps_object){x.detector_fps_object = e.details.detector_fps_object}
x.pipe += ' -r ' + x.detector_fps_object + x.dobjratio + x.cust_detect
if(e.details.detector_h264 === '1'){
x.pipe += h264Output
}else{
@ -884,6 +907,46 @@ module.exports = function(s,config,onFinish){
x.pipe += ' -q:v 1 -an -c:v copy -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'coProcessor.m3u8"'
}
}
ffmpeg.buildTimelapseOutput = function(e,x){
if(e.details.record_timelapse === '1'){
x.record_timelapse_video_filters = []
if(e.details.input_map_choices&&e.details.input_map_choices.record_timelapse){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.record_timelapse)
}
var flags = []
if(e.details.record_timelapse_fps && e.details.record_timelapse_fps !== ''){
flags.push('-r 1/' + e.details.record_timelapse_fps)
}else{
flags.push('-r 1/900') // 15 minutes
}
if(e.details.record_timelapse_vf && e.details.record_timelapse_vf !== '')flags.push('-vf ' + e.details.record_timelapse_vf)
if(e.details.record_timelapse_scale_x && e.details.record_timelapse_scale_x !== '' && e.details.record_timelapse_scale_y && e.details.record_timelapse_scale_y !== '')flags.push(`-s ${e.details.record_timelapse_scale_x}x${e.details.record_timelapse_scale_y}`)
//record - watermark for -vf
if(e.details.record_timelapse_watermark&&e.details.record_timelapse_watermark=="1"&&e.details.record_timelapse_watermark_location&&e.details.record_timelapse_watermark_location!==''){
switch(e.details.record_timelapse_watermark_position){
case'tl'://top left
x.record_timelapse_watermark_position='10:10'
break;
case'tr'://top right
x.record_timelapse_watermark_position='main_w-overlay_w-10:10'
break;
case'bl'://bottom left
x.record_timelapse_watermark_position='10:main_h-overlay_h-10'
break;
default://bottom right
x.record_timelapse_watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2'
break;
}
x.record_timelapse_video_filters.push('movie='+e.details.record_timelapse_watermark_location+'[watermark],[in][watermark]overlay='+x.record_timelapse_watermark_position+'[out]');
}
if(x.record_timelapse_video_filters.length > 0){
var videoFilter = `-vf "${x.record_timelapse_video_filters.join(',').trim()}"`
flags.push(videoFilter)
}
x.pipe += ` -f singlejpeg ${flags.join(' ')} -an -q:v 1 pipe:7`
}
}
ffmpeg.assembleMainPieces = function(e,x){
//create executeable FFMPEG command
x.ffmpegCommandString = x.loglevel+x.input_fps;
@ -900,6 +963,9 @@ module.exports = function(s,config,onFinish){
case'mjpeg':
x.ffmpegCommandString += ' -reconnect 1 -f mjpeg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"';
break;
case'mxpeg':
x.ffmpegCommandString += ' -reconnect 1 -f mxg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"';
break;
case'rtmp':
if(!e.details.rtmp_key)e.details.rtmp_key = ''
x.ffmpegCommandString += x.cust_input+x.hwaccel+` -i "rtmp://127.0.0.1:1935/${e.ke + '_' + e.mid + '_' + e.details.rtmp_key}"`;
@ -941,13 +1007,14 @@ module.exports = function(s,config,onFinish){
ffmpeg.buildAudioDetector(e,x)
ffmpeg.buildMainDetector(e,x)
ffmpeg.buildCoProcessorFeed(e,x)
ffmpeg.buildTimelapseOutput(e,x)
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
extender(e,x)
})
ffmpeg.assembleMainPieces(e,x)
ffmpeg.createPipeArray(e,x)
//hold ffmpeg command for log stream
s.group[e.ke].mon[e.mid].ffmpeg = x.ffmpegCommandString
s.group[e.ke].activeMonitors[e.mid].ffmpeg = x.ffmpegCommandString
//clean the string of spatial impurities and split for spawn()
x.ffmpegCommandString = s.splitForFFPMEG(x.ffmpegCommandString)
//launch that bad boy

View file

@ -140,27 +140,27 @@ module.exports = function(s,config,lang,ffmpeg){
if(commandString === x.input){
return false
}
s.group[e.ke].mon[e.mid].coProcessorCmd = commandString
s.group[e.ke].activeMonitors[e.mid].coProcessorCmd = commandString
return spawn(config.ffmpegDir,s.splitForFFPMEG((commandString).replace(/\s+/g,' ').trim()),{detached: true,stdio:x.stdioPipes})
}
s.coSpawnLauncher = function(e){
if(s.group[e.ke].mon[e.id].isStarted === true && e.coProcessor === true){
if(s.group[e.ke].activeMonitors[e.id].isStarted === true && e.coProcessor === true){
s.coSpawnClose(e)
s.group[e.ke].mon[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e)
if(s.group[e.ke].mon[e.id].coSpawnProcessor === false){
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e)
if(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor === false){
return
}
s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}});
s.group[e.ke].mon[e.id].coSpawnProcessorExit = function(){
s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}});
s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].activeMonitors[e.id].coProcessorCmd}});
s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit = function(){
s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].activeMonitors[e.id].coProcessorCmd}});
setTimeout(function(){
s.coSpawnLauncher(e)
},2000)
}
s.group[e.ke].mon[e.id].coSpawnProcessor.on('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit)
s.group[e.ke].mon[e.id].coSpawnProcessor.on('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit)
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.on('end',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit)
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.on('exit',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit)
var checkLog = function(d,x){return d.indexOf(x)>-1;}
s.group[e.ke].mon[e.id].coSpawnProcessor.stderr.on('data',function(d){
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stderr.on('data',function(d){
d=d.toString();
switch(true){
case checkLog(d,'deprecated pixel format used'):
@ -176,34 +176,34 @@ module.exports = function(s,config,lang,ffmpeg){
s.userLog(e,{type:lang.coProcessor,msg:d});
})
if(e.frame_to_stream){
s.group[e.ke].mon[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream)
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream)
}
if(e.details.detector === '1'){
s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke})
//frames from motion detect
if(e.details.detector_pam === '1'){
s.createPamDiffEngine(e)
s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].mon[e.id].p2p).pipe(s.group[e.ke].mon[e.id].pamDiff)
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff)
if(e.details.detector_use_detect_object === '1'){
s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[4].on('data',function(d){
s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[4].on('data',function(d){
s.group[e.ke].activeMonitors[e.id].lastJpegDetectorFrame = d
})
}
}else if(s.isAtleatOneDetectorPluginConnected){
s.group[e.ke].mon[e.id].coSpawnProcessor.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});
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdio[3].on('data',function(d){
s.ocvTx({f:'frame',mon:s.group[e.ke].rawMonitorConfigurations[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d});
})
}
}
}
}
s.coSpawnClose = function(e){
if(s.group[e.ke].mon[e.id].coSpawnProcessor){
s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit);
s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit);
s.group[e.ke].mon[e.id].coSpawnProcessor.stdin.pause()
s.group[e.ke].mon[e.id].coSpawnProcessor.kill()
delete(s.group[e.ke].mon[e.id].coSpawnProcessor)
if(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor){
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit);
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].activeMonitors[e.id].coSpawnProcessorExit);
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.stdin.pause()
s.group[e.ke].activeMonitors[e.id].coSpawnProcessor.kill()
delete(s.group[e.ke].activeMonitors[e.id].coSpawnProcessor)
s.userLog(e,{type:lang['coProcessor Stopped'],msg:{msg:lang.coProcessorTextStopped+' : '+e.id}});
}
}

13
libs/fileBin.js Normal file
View file

@ -0,0 +1,13 @@
var fs = require('fs')
var moment = require('moment')
module.exports = function(s,config,lang,app,io){
s.getFileBinDirectory = function(e){
if(e.mid&&!e.id){e.id=e.mid}
s.checkDetails(e)
if(e.details&&e.details.dir&&e.details.dir!==''){
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'/'
}else{
return s.dir.fileBin+e.ke+'/'+e.id+'/';
}
}
}

View file

@ -1,5 +1,5 @@
var fs = require('fs');
module.exports = function(s,config){
module.exports = function(s,config,lang){
//directories
s.group = {}
if(!config.windowsTempDir&&s.isWin===true){config.windowsTempDir='C:/Windows/Temp'}
@ -40,10 +40,44 @@ module.exports = function(s,config){
fs.mkdirSync(s.dir.fileBin);
}
//additional storage areas
s.listOfStorage = [{
name: lang['Default'],
value: ""
}]
s.dir.addStorage.forEach(function(v,n){
v.path = s.checkCorrectPathEnding(v.path)
if(!fs.existsSync(v.path)){
fs.mkdirSync(v.path);
}
s.listOfStorage.push({
name: v.name,
value: v.path
})
})
//get audio files list
s.listOfAudioFiles = [
{
name:lang['No Sound'],
value:""
}
]
fs.readdirSync(s.mainDirectory + '/web/libs/audio').forEach(function(file){
s.listOfAudioFiles.push({
name: file,
value: file
})
})
//get themes list
s.listOfThemes = [
{
name:lang['Default'],
value:""
}
]
fs.readdirSync(s.mainDirectory + '/web/libs/themes').forEach(function(folder){
s.listOfThemes.push({
name: folder,
value: folder
})
})
}

View file

@ -1,12 +1,6 @@
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
module.exports = function(s,config,lang,io){
s.sendDiskUsedAmountToClients = function(e){
//send the amount used disk space to connected users
if(s.group[e.ke]&&s.group[e.ke].init){
s.tx({f:'diskUsed',size:s.group[e.ke].usedSpace,limit:s.group[e.ke].sizeLimit},'GRP_'+e.ke);
}
}
s.heartBeat = function(){
setTimeout(s.heartBeat, 8000);
io.sockets.emit('ping',{beat:1});
@ -34,14 +28,20 @@ module.exports = function(s,config,lang,io){
d = d.replace(/(\r\n|\n|\r)/gm, "").replace(/%/g, "")
}
callback(d)
});
s.onGetCpuUsageExtensions.forEach(function(extender){
extender(d)
})
})
} else if(k.cmd){
exec(k.cmd,{encoding:'utf8',detached: true},function(err,d){
if(s.isWin===true){
d=d.replace(/(\r\n|\n|\r)/gm,"").replace(/%/g,"")
}
callback(d)
});
s.onGetCpuUsageExtensions.forEach(function(extender){
extender(d)
})
})
} else {
callback(0)
}
@ -68,7 +68,10 @@ module.exports = function(s,config,lang,io){
d=(parseInt(d.split('=')[1])/(s.totalmem/1000))*100
}
callback(d)
});
s.onGetRamUsageExtensions.forEach(function(extender){
extender(d)
})
})
}else{
callback(0)
}

View file

@ -1,3 +1,4 @@
var fs = require('fs')
module.exports = function(s,config){
if(!config.language){
config.language='en_CA'
@ -9,19 +10,19 @@ module.exports = function(s,config){
console.log('There was an error loading your language file.')
var lang = require(s.location.languages+'/en_CA.json');
}
s.location.definitions = s.mainDirectory+'/definitions'
try{
var definitions = require(s.location.definitions+'/'+config.language+'.json');
}catch(er){
console.error(er)
console.log('There was an error loading your language file.')
var definitions = require(s.location.definitions+'/en_CA.json');
}
//load languages dynamically
s.copySystemDefaultLanguage = function(){
//en_CA
return Object.assign(lang,{})
}
s.listOfPossibleLanguages = []
fs.readdirSync(s.mainDirectory + '/languages').forEach(function(filename){
var name = filename.replace('.json','')
s.listOfPossibleLanguages.push({
"name": name,
"value": name,
})
})
s.loadedLanguages={}
s.loadedLanguages[config.language] = s.copySystemDefaultLanguage()
s.getLanguageFile = function(rule){
@ -41,29 +42,5 @@ module.exports = function(s,config){
}
return file
}
//load defintions dynamically
s.copySystemDefaultDefinitions = function(){
//en_CA
return Object.assign(definitions,{})
}
s.loadedDefinitons={}
s.loadedDefinitons[config.language] = s.copySystemDefaultDefinitions()
s.getDefinitonFile = function(rule){
if(rule && rule !== ''){
var file = s.loadedDefinitons[file]
if(!file){
try{
s.loadedDefinitons[rule] = require(s.location.definitions+'/'+rule+'.json')
s.loadedDefinitons[rule] = Object.assign(s.copySystemDefaultDefinitions(),s.loadedDefinitons[rule])
file = s.loadedDefinitons[rule]
}catch(err){
file = s.copySystemDefaultDefinitions()
}
}
}else{
file = s.copySystemDefaultDefinitions()
}
return file
}
return lang
}

File diff suppressed because it is too large Load diff

View file

@ -50,7 +50,7 @@ module.exports = function(s,config,lang){
var onEventTriggerForDiscord = function(d,filter){
// d = event object
//discord bot
if(filter.discord && d.mon.details.detector_discordbot === '1' && !s.group[d.ke].mon[d.id].detector_discordbot){
if(filter.discord && s.group[d.ke].discordBot && d.mon.details.detector_discordbot === '1' && !s.group[d.ke].activeMonitors[d.id].detector_discordbot){
var detector_discordbot_timeout
if(!d.mon.details.detector_discordbot_timeout||d.mon.details.detector_discordbot_timeout===''){
detector_discordbot_timeout = 1000*60*10;
@ -58,16 +58,16 @@ module.exports = function(s,config,lang){
detector_discordbot_timeout = parseFloat(d.mon.details.detector_discordbot_timeout)*1000*60;
}
//lock mailer so you don't get emailed on EVERY trigger event.
s.group[d.ke].mon[d.id].detector_discordbot=setTimeout(function(){
s.group[d.ke].activeMonitors[d.id].detector_discordbot = setTimeout(function(){
//unlock so you can mail again.
clearTimeout(s.group[d.ke].mon[d.id].detector_discordbot);
delete(s.group[d.ke].mon[d.id].detector_discordbot);
},detector_discordbot_timeout);
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_discordbot);
delete(s.group[d.ke].activeMonitors[d.id].detector_discordbot);
},detector_discordbot_timeout)
var files = []
var sendAlert = function(){
s.discordMsg({
author: {
name: s.group[d.ke].mon_conf[d.id].name,
name: s.group[d.ke].rawMonitorConfigurations[d.id].name,
icon_url: config.iconURL
},
title: lang.Event+' - '+d.screenshotName,
@ -84,7 +84,7 @@ module.exports = function(s,config,lang){
s.mergeDetectorBufferChunks(d,function(mergedFilepath,filename){
s.discordMsg({
author: {
name: s.group[d.ke].mon_conf[d.id].name,
name: s.group[d.ke].rawMonitorConfigurations[d.id].name,
icon_url: config.iconURL
},
title: filename,
@ -102,8 +102,10 @@ module.exports = function(s,config,lang){
],d.ke)
})
}
s.getRawSnapshotFromMonitor(d.mon,function(data){
if((data[data.length-2] === 0xFF && data[data.length-1] === 0xD9)){
s.getRawSnapshotFromMonitor(d.mon,{
secondsInward: d.mon.details.snap_seconds_inward
},function(data){
if(data[data.length - 2] === 0xFF && data[data.length - 1] === 0xD9){
d.screenshotBuffer = data
files.push({
attachment: d.screenshotBuffer,
@ -251,7 +253,7 @@ module.exports = function(s,config,lang){
filter.mail = true
}
var onEventTriggerForEmail = function(d,filter){
if(filter.mail && config.mail && !s.group[d.ke].mon[d.id].detector_mail && d.mon.details.detector_mail === '1'){
if(filter.mail && config.mail && !s.group[d.ke].activeMonitors[d.id].detector_mail && d.mon.details.detector_mail === '1'){
s.sqlQuery('SELECT mail FROM Users WHERE ke=? AND details NOT LIKE ?',[d.ke,'%"sub"%'],function(err,r){
r=r[0];
var detector_mail_timeout
@ -261,10 +263,10 @@ module.exports = function(s,config,lang){
detector_mail_timeout = parseFloat(d.mon.details.detector_mail_timeout)*1000*60;
}
//lock mailer so you don't get emailed on EVERY trigger event.
s.group[d.ke].mon[d.id].detector_mail=setTimeout(function(){
s.group[d.ke].activeMonitors[d.id].detector_mail=setTimeout(function(){
//unlock so you can mail again.
clearTimeout(s.group[d.ke].mon[d.id].detector_mail);
delete(s.group[d.ke].mon[d.id].detector_mail);
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_mail);
delete(s.group[d.ke].activeMonitors[d.id].detector_mail);
},detector_mail_timeout);
var files = []
var mailOptions = {
@ -287,36 +289,42 @@ module.exports = function(s,config,lang){
}
if(d.mon.details.detector_mail_send_video === '1'){
s.mergeDetectorBufferChunks(d,function(mergedFilepath,filename){
s.nodemailer.sendMail({
from: config.mail.from,
to: r.mail,
subject: filename,
html: '',
attachments: [
{
filename: filename,
content: fs.readFileSync(mergedFilepath)
}
]
}, (error, info) => {
if (error) {
s.systemLog(lang.MailError,error)
return false;
fs.readFile(mergedFilepath,function(err,buffer){
if(buffer){
s.nodemailer.sendMail({
from: config.mail.from,
to: r.mail,
subject: filename,
html: '',
attachments: [
{
filename: filename,
content: buffer
}
]
}, (error, info) => {
if (error) {
s.systemLog(lang.MailError,error)
return false;
}
})
}
})
})
}
if(d.screenshotBuffer){
files.push({
filename: d.screenshotName+'.jpg',
filename: d.screenshotName + '.jpg',
content: d.screenshotBuffer
})
sendMail()
}else{
s.getRawSnapshotFromMonitor(d.mon,function(data){
s.getRawSnapshotFromMonitor(d.mon,{
secondsInward: d.mon.details.snap_seconds_inward
},function(data){
d.screenshotBuffer = data
files.push({
filename: d.screenshotName+'.jpg',
filename: d.screenshotName + '.jpg',
content: data
})
sendMail()

View file

@ -1,7 +1,12 @@
var socketIOclient = require('socket.io-client');
module.exports = function(s,config,lang){
module.exports = function(s,config,lang,io){
//send data to detector plugin
s.ocvTx = function(data){
// chaining coming in future update
s.sendToAllDetectors(data)
}
//function for receiving detector data
s.pluginEventController=function(d){
s.pluginEventController = function(d){
switch(d.f){
case'trigger':
s.triggerEvent(d)
@ -22,16 +27,32 @@ module.exports = function(s,config,lang){
s.detectorPluginArray = []
s.isAtleatOneDetectorPluginConnected = false
s.addDetectorPlugin = function(name,d){
if(config.useOldPluginConnectionMethod === true){
s.ocv = {
started: s.timeObject(),
id: d.id,
plug: d.plug,
notice: d.notice,
isClientPlugin: d.isClientPlugin,
isHostPlugin: d.isHostPlugin,
connectionType: d.connectionType
}
}
s.connectedDetectorPlugins[d.plug] = {
started: s.timeObject(),
id: d.id,
plug: d.plug,
notice: d.notice,
isClientPlugin: d.isClientPlugin,
isHostPlugin: d.isHostPlugin,
connectionType: d.connectionType
}
s.resetDetectorPluginArray()
}
s.removeDetectorPlugin = function(name){
if(config.oldPluginConnectionMethod === true && s.ocv && s.ocv.plug === name){
delete(s.ocv)
}
delete(s.connectedDetectorPlugins[name])
s.resetDetectorPluginArray(name)
}
@ -48,6 +69,7 @@ module.exports = function(s,config,lang){
}else{
s.isAtleatOneDetectorPluginConnected = false
}
s.debugLog(`resetDetectorPluginArray : ${JSON.stringify(pluginArray)}`)
s.detectorPluginArray = pluginArray
}
s.sendToAllDetectors = function(data){
@ -67,7 +89,7 @@ module.exports = function(s,config,lang){
// s.sendToDetectorsInChain = function(){
//
// }
s.pluginInitiatorSuccess=function(mode,d,cn){
s.pluginInitiatorSuccess = function(mode,d,cn){
s.systemLog('pluginInitiatorSuccess',d)
if(!s.connectedPlugins[d.plug]){
s.connectedPlugins[d.plug]={
@ -83,6 +105,7 @@ module.exports = function(s,config,lang){
s.systemLog('Connected to plugin : Detector - '+d.plug+' - '+d.type)
switch(d.type){
default:case'detector':
if(config.oldPluginConnectionMethod)cn.ocv = 1
cn.detectorPlugin = d.plug
s.addDetectorPlugin(d.plug,{
id: cn.id,
@ -99,10 +122,10 @@ module.exports = function(s,config,lang){
switch(d.type){
default:case'detector':
s.addDetectorPlugin(d.plug,{
id:"host",
plug:d.plug,
notice:d.notice,
isHostPlugin:true,
id: "host",
plug: d.plug,
notice: d.notice,
isHostPlugin: true,
connectionType: d.connectionType
})
s.tx({f:'detector_plugged',plug:d.plug,notice:d.notice},'CPU')
@ -145,12 +168,12 @@ module.exports = function(s,config,lang){
});
socket.on('init',function(d){
s.systemLog('Initialize Plugin : Host',d)
if(d.ok===true){
if(d.ok === true){
s.pluginInitiatorSuccess("host",d)
}else{
s.pluginInitiatorFail("host",d)
}
});
})
socket.on('ocv',s.pluginEventController);
socket.on('disconnect', function(){
s.connectedPlugins[v.id].plugged=false
@ -174,4 +197,70 @@ module.exports = function(s,config,lang){
}
})
}
var onWebSocketDisconnection = function(cn){
if(cn.pluginEngine){
s.connectedPlugins[cn.pluginEngine].plugged = false
s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU')
}
if(cn.detectorPlugin){
s.tx({f:'detector_unplugged',plug:cn.detectorPlugin},'CPU')
s.removeDetectorPlugin(cn.detectorPlugin)
s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){
s.tx(data,'CPU')
})
}
if(cn.ocv && s.ocv){
s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU')
delete(s.ocv);
}
}
var onSocketAuthentication = function(r,cn,d,tx){
if(s.isAtleatOneDetectorPluginConnected){
s.sendDetectorInfoToClient({f:'detector_plugged'},tx)
s.ocvTx({f:'readPlugins',ke:d.ke})
}
if(config.oldPluginConnectionMethod && s.ocv){
tx({f:'detector_plugged',plug:s.ocv.plug,notice:s.ocv.notice})
}
}
var onWebSocketConnection = function(cn){
cn.on('ocv',function(d){
if(!cn.pluginEngine && d.f === 'init'){
if(config.pluginKeys[d.plug] === d.pluginKey){
s.pluginInitiatorSuccess("client",d,cn)
}else{
s.pluginInitiatorFail("client",d,cn)
}
}else{
if(config.pluginKeys[d.plug] === d.pluginKey){
s.pluginEventController(d)
}else{
cn.disconnect()
}
}
})
cn.on('f',function(d){
if((d.id || d.uid || d.mid) && cn.ke){
switch(d.f){
case'ocv_in':
s.ocvTx(d.data)
break;
}
}
})
}
if(config.oldPluginConnectionMethod === undefined)config.oldPluginConnectionMethod = false
if(config.oldPluginConnectionMethod === true){
s.ocvTx = function(data){
if(!s.ocv){return}
if(s.ocv.isClientPlugin === true){
s.tx(data,s.ocv.id)
}else{
s.connectedPlugins[s.ocv.plug].tx(data)
}
}
}
s.onSocketAuthentication(onSocketAuthentication)
s.onWebSocketDisconnection(onWebSocketDisconnection)
s.onWebSocketConnection(onWebSocketConnection)
}

View file

@ -1,5 +1,6 @@
var os = require('os')
module.exports = function(process,__dirname){
var packageJson = require('../package.json')
process.send = process.send || function () {};
process.on('uncaughtException', function (err) {
console.error('Uncaught Exception occured!');
@ -32,5 +33,9 @@ module.exports = function(process,__dirname){
//directory path for this file
mainDirectory : __dirname
}
s.packageJson = packageJson
if(packageJson.mainDirectory){
s.mainDirectory = require('path').resolve('.')
}
return s
}

View file

@ -1,5 +1,3 @@
const chalk = require('chalk');
LOG_TYPES = {
NONE: 0,
ERROR: 1,
@ -24,25 +22,25 @@ const logTime = () => {
const log = (...args) => {
if (logType < LOG_TYPES.NORMAL) return;
console.log(logTime(), process.pid, chalk.bold.green('[INFO]'), ...args);
console.log(logTime(), ...args);
};
const error = (...args) => {
if (logType < LOG_TYPES.ERROR) return;
console.log(logTime(), process.pid, chalk.bold.red('[ERROR]'), ...args);
console.error(logTime(), ...args);
};
const debug = (...args) => {
if (logType < LOG_TYPES.DEBUG) return;
console.log(logTime(), process.pid, chalk.bold.blue('[DEBUG]'), ...args);
console.log(logTime(), ...args);
};
const ffdebug = (...args) => {
if (logType < LOG_TYPES.FFDEBUG) return;
console.log(logTime(), process.pid, chalk.bold.blue('[FFDEBUG]'), ...args);
console.log(logTime(), ...args);
};
module.exports = {
@ -50,4 +48,4 @@ module.exports = {
setLogType,
log, error, debug, ffdebug
}
}

View file

@ -43,7 +43,7 @@ class NodeRtmpServer {
session.socket.destroy();
context.sessions.delete(id);
}
});
})
}
}

View file

@ -15,6 +15,10 @@ module.exports = function(s,config,lang,app,io){
var schedule = Object.assign(row,{})
if(!s.schedules[schedule.ke])s.schedules[schedule.ke] = {}
s.checkDetails(schedule)
schedule.timezoneOffset = parseInt(schedule.details.timezone) || 0
schedule.details.days.forEach(function(dayNumber,key){
schedule.details.days[key] = parseInt(dayNumber)
})
if(!s.schedules[schedule.ke][schedule.name]){
s.schedules[schedule.ke][schedule.name] = schedule
}else{
@ -22,7 +26,10 @@ module.exports = function(s,config,lang,app,io){
}
}
//check time in schedule
s.checkTimeAgainstSchedule = function(start,end,callback){
var checkTimeAgainstSchedule = function(schedule,callback){
var start = schedule.start
var end = schedule.end
if(!callback)callback = function(){}
try{
if(
start
@ -32,16 +39,17 @@ module.exports = function(s,config,lang,app,io){
var startHour = parseInt(startSplit[0])
var startMin = parseInt(startSplit[1])
checkStartTime.setHours(startHour)
checkStartTime.setMinutes(startMin)
checkStartTime.setMinutes(startMin - schedule.timezoneOffset)
if(end){
var checkEndTime = new Date()
var endSplit = end.split(':')
var endHour = parseInt(endSplit[0])
var endMin = parseInt(endSplit[1])
checkEndTime.setHours(endHour)
checkEndTime.setMinutes(endMin)
checkEndTime.setMinutes(endMin - schedule.timezoneOffset)
}
var currentDate = new Date()
currentDate.setMinutes(currentDate.getMinutes() - schedule.timezoneOffset)
if(
(
currentDate >= checkStartTime &&
@ -50,6 +58,7 @@ module.exports = function(s,config,lang,app,io){
currentDate >= checkStartTime && !end
){
callback()
return true
}else{
callback({
currentDate : currentDate,
@ -58,12 +67,45 @@ module.exports = function(s,config,lang,app,io){
})
}
}else{
callback()
callback({}) //no start time selected, failed
}
}catch(err){
console.log(err)
callback()
callback(err)
}
return false
}
//check days in schedule
var checkDaysAgainstSchedule = function(schedule,callback){
var days = schedule.details.days
if(!callback)callback = function(){}
try{
if(
days
){
var currentDate = new Date()
currentDate.setMinutes(currentDate.getMinutes() - schedule.timezoneOffset)
var currentDay = currentDate.getDay()
if(
days.indexOf(currentDay) > -1 // if currentDay of week is found in schedule selection
){
callback()
return true
}else{
callback({
currentDate: currentDate,
currentDay: currentDay
})
}
}else{
callback() //no days selected, succeed
return true
}
}catch(err){
console.log(err)
callback(err)
}
return false
}
//check all Schedules
s.checkSchedules = function(v,callback){
@ -73,25 +115,25 @@ module.exports = function(s,config,lang,app,io){
scheduleNames.forEach(function(name){
var schedule = s.schedules[key][name]
if(!schedule.active && schedule.enabled === 1 && schedule.start && schedule.details.monitorStates){
s.checkTimeAgainstSchedule(schedule.start,schedule.end,function(err){
if(!err){
schedule.active = true
var monitorStates = schedule.details.monitorStates
monitorStates.forEach(function(stateName){
s.activateMonitorStates(key,stateName,{
ke: key,
uid: 'System',
details: {},
permissions: {},
lang: lang
},function(endData){
// console.log(endData)
})
var timePasses = checkTimeAgainstSchedule(schedule)
var daysPasses = checkDaysAgainstSchedule(schedule)
if(timePasses && daysPasses){
schedule.active = true
var monitorStates = schedule.details.monitorStates
monitorStates.forEach(function(stateName){
s.activateMonitorStates(key,stateName,{
ke: key,
uid: 'System',
details: {},
permissions: {},
lang: lang
},function(endData){
// console.log(endData)
})
}else{
schedule.active = false
}
})
})
}else{
schedule.active = false
}
}
})
})

View file

@ -7,11 +7,6 @@ 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){
// chaining coming in future update
s.sendToAllDetectors(data)
}
//send data to socket client function
s.tx = function(z,y,x){if(x){return x.broadcast.to(y).emit('f',z)};io.to(y).emit('f',z);}
s.txToDashcamUsers = function(data,groupKey){
@ -53,25 +48,9 @@ module.exports = function(s,config,lang,io){
////socket controller
io.on('connection', function (cn) {
var tx;
//set "client" detector plugin event function
cn.on('ocv',function(d){
if(!cn.pluginEngine && d.f === 'init'){
if(config.pluginKeys[d.plug] === d.pluginKey){
s.pluginInitiatorSuccess("client",d,cn)
}else{
s.pluginInitiatorFail("client",d,cn)
}
}else{
if(config.pluginKeys[d.plug] === d.pluginKey){
s.pluginEventController(d)
}else{
cn.disconnect()
}
}
})
//unique h265 socket stream
cn.on('h265',function(d){
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
cn.disconnect();return;
}
cn.ip=cn.request.connection.remoteAddress;
@ -87,10 +66,10 @@ module.exports = function(s,config,lang,io){
r=r[0];
var Emitter,chunkChannel
if(!d.channel){
Emitter = s.group[d.ke].mon[d.id].emitter
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
chunkChannel = parseInt(d.channel)+config.pipeAddition
}
if(!Emitter){
@ -144,7 +123,7 @@ module.exports = function(s,config,lang,io){
})
//unique Base64 socket stream
cn.on('Base64',function(d){
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
cn.disconnect();return;
}
cn.ip=cn.request.connection.remoteAddress;
@ -160,10 +139,10 @@ module.exports = function(s,config,lang,io){
r=r[0];
var Emitter,chunkChannel
if(!d.channel){
Emitter = s.group[d.ke].mon[d.id].emitter
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
chunkChannel = parseInt(d.channel)+config.pipeAddition
}
if(!Emitter){
@ -217,7 +196,7 @@ module.exports = function(s,config,lang,io){
})
//unique FLV socket stream
cn.on('FLV',function(d){
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
cn.disconnect();return;
}
cn.ip=cn.request.connection.remoteAddress;
@ -233,10 +212,10 @@ module.exports = function(s,config,lang,io){
r=r[0];
var Emitter,chunkChannel
if(!d.channel){
Emitter = s.group[d.ke].mon[d.id].emitter
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
chunkChannel = parseInt(d.channel)+config.pipeAddition
}
if(!Emitter){
@ -253,7 +232,7 @@ module.exports = function(s,config,lang,io){
cn.closeSocketVideoStream = function(){
Emitter.removeListener('data', contentWriter);
}
tx({time:toUTC(),buffer:s.group[d.ke].mon[d.id].firstStreamChunk[chunkChannel]})
tx({time:toUTC(),buffer:s.group[d.ke].activeMonitors[d.id].firstStreamChunk[chunkChannel]})
Emitter.on('data',contentWriter = function(buffer){
tx({time:toUTC(),buffer:buffer})
})
@ -290,7 +269,7 @@ module.exports = function(s,config,lang,io){
})
//unique MP4 socket stream
cn.on('MP4',function(d){
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){
cn.disconnect();return;
}
cn.ip=cn.request.connection.remoteAddress;
@ -306,10 +285,10 @@ module.exports = function(s,config,lang,io){
r=r[0];
var Emitter,chunkChannel
if(!d.channel){
Emitter = s.group[d.ke].mon[d.id].emitter
Emitter = s.group[d.ke].activeMonitors[d.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
chunkChannel = parseInt(d.channel)+config.pipeAddition
}
if(!Emitter){
@ -321,7 +300,7 @@ module.exports = function(s,config,lang,io){
cn.auth=d.auth;
cn.channel=d.channel;
cn.socketVideoStream=d.id;
var mp4frag = s.group[d.ke].mon[d.id].mp4frag[d.channel];
var mp4frag = s.group[d.ke].activeMonitors[d.id].mp4frag[d.channel];
var onInitialized = () => {
cn.emit('mime', mp4frag.mime);
mp4frag.removeListener('initialized', onInitialized);
@ -438,13 +417,9 @@ module.exports = function(s,config,lang,io){
}
s.group[d.ke].users[d.auth].lang=s.getLanguageFile(s.group[d.ke].users[d.auth].details.lang)
s.userLog({ke:d.ke,mid:'$USER'},{type:s.group[d.ke].users[d.auth].lang['Websocket Connected'],msg:{mail:r.mail,id:d.uid,ip:cn.ip}})
if(!s.group[d.ke].mon){
s.group[d.ke].mon={}
if(!s.group[d.ke].mon){s.group[d.ke].mon={}}
}
if(s.isAtleatOneDetectorPluginConnected){
s.sendDetectorInfoToClient({f:'detector_plugged'},tx)
s.ocvTx({f:'readPlugins',ke:d.ke})
if(!s.group[d.ke].activeMonitors){
s.group[d.ke].activeMonitors={}
if(!s.group[d.ke].activeMonitors){s.group[d.ke].activeMonitors={}}
}
tx({f:'users_online',users:s.group[d.ke].users})
s.tx({f:'user_status_change',ke:d.ke,uid:cn.uid,status:1,user:s.group[d.ke].users[d.auth]},'GRP_'+d.ke)
@ -465,7 +440,7 @@ module.exports = function(s,config,lang,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=?', [d.ke], function(err,r) {
if(r && r[0]){
r.forEach(function(monitor){
s.cameraSendSnapshot({mid:monitor.mid,ke:monitor.ke,mon:monitor})
s.cameraSendSnapshot({mid:monitor.mid,ke:monitor.ke,mon:monitor},{useIcon: true})
})
}
})
@ -474,7 +449,7 @@ module.exports = function(s,config,lang,io){
}
})
s.onSocketAuthenticationExtensions.forEach(function(extender){
extender(r,cn)
extender(r,cn,d,tx)
})
}
s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) {
@ -507,9 +482,6 @@ module.exports = function(s,config,lang,io){
if((d.id||d.uid||d.mid)&&cn.ke){
try{
switch(d.f){
case'ocv_in':
s.ocvTx(d.data)
break;
case'monitorOrder':
if(d.monitorOrder && d.monitorOrder instanceof Object){
s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){
@ -744,9 +716,10 @@ module.exports = function(s,config,lang,io){
getVideos(function(videos){
getEvents(function(events){
tx({
f:'drawPowerVideoMainTimeLine',
videos:videos,
events:events
f: 'videos&events',
id: d.mid,
videos: videos,
events: events
})
})
})
@ -781,7 +754,7 @@ module.exports = function(s,config,lang,io){
case'watch_on':
if(!d.ke){d.ke=cn.ke}
s.initiateMonitorObject({mid:d.id,ke:d.ke});
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]||s.group[d.ke].mon[d.id].isStarted === false){return false}
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false}
cn.join('MON_'+d.ke+d.id);
cn.join('DETECTOR_'+d.ke+d.id);
if(cn.jpeg_on !== true){
@ -951,6 +924,7 @@ module.exports = function(s,config,lang,io){
var tempSessionKey = s.gid(30)
cn.superSessionKey = tempSessionKey
s.superUsersApi[tempSessionKey] = data
s.superUsersApi[tempSessionKey].cnid = cn.id
if(!data.$user.tokens)data.$user.tokens = {}
data.$user.tokens[tempSessionKey] = {}
cn.ip=cn.request.connection.remoteAddress
@ -1229,15 +1203,15 @@ module.exports = function(s,config,lang,io){
login_type:'Streamer'
}
s.group[d.ke].dashcamUsers[d.auth] = s.group[d.ke].users[d.auth]
if(s.group[d.ke].mon){
Object.keys(s.group[d.ke].mon).forEach(function(monitorId){
if(s.group[d.ke].activeMonitors){
Object.keys(s.group[d.ke].activeMonitors).forEach(function(monitorId){
var dataToClient = {
f : 'disable_stream',
mid : monitorId,
ke : d.ke
}
var mon = s.group[d.ke].mon[monitorId]
if(s.group[d.ke].mon_conf[monitorId].type === 'dashcam'){
var mon = s.group[d.ke].activeMonitors[monitorId]
if(s.group[d.ke].rawMonitorConfigurations[monitorId].type === 'dashcam'){
if(mon.allowStdinWrite === true){
dataToClient.f = 'enable_stream'
}
@ -1248,22 +1222,22 @@ module.exports = function(s,config,lang,io){
}
})
}else{
if(s.group[d.ke] && s.group[d.ke].mon[d.mid]){
if(s.group[d.ke].mon[d.mid].allowStdinWrite === true){
if(s.group[d.ke] && s.group[d.ke].activeMonitors[d.mid]){
if(s.group[d.ke].activeMonitors[d.mid].allowStdinWrite === true){
switch(d.f){
case'monitor_chunk':
if(s.group[d.ke].mon[d.mid].isStarted !== true || !s.group[d.ke].mon[d.mid].spawn || !s.group[d.ke].mon[d.mid].spawn.stdin){
if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true || !s.group[d.ke].activeMonitors[d.mid].spawn || !s.group[d.ke].activeMonitors[d.mid].spawn.stdin){
s.tx({error:'Not Started'},cn.id);
return false
};
s.group[d.ke].mon[d.mid].spawn.stdin.write(new Buffer(d.chunk, "binary"));
s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(new Buffer(d.chunk, "binary"));
break;
case'monitor_frame':
if(s.group[d.ke].mon[d.mid].isStarted !== true){
if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true){
s.tx({error:'Not Started'},cn.id);
return false
};
s.group[d.ke].mon[d.mid].spawn.stdin.write(d.frame);
s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(d.frame);
break;
}
}else{
@ -1279,7 +1253,7 @@ module.exports = function(s,config,lang,io){
tx=function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);}
switch(d.f){
case'init':
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]||s.group[d.ke].mon[d.id].isStarted === false){return false}
if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false}
s.auth({auth:d.auth,ke:d.ke,id:d.id,ip:cn.request.connection.remoteAddress},function(user){
cn.embedded=1;
cn.ke=d.ke;
@ -1294,10 +1268,10 @@ module.exports = function(s,config,lang,io){
cn.join('MON_STREAM_'+d.ke+d.id);
cn.join('DETECTOR_'+d.ke+d.id);
cn.join('STR_'+d.ke);
if(s.group[d.ke]&&s.group[d.ke].mon[d.id]&&s.group[d.ke].mon[d.id].watch){
if(s.group[d.ke]&&s.group[d.ke].activeMonitors[d.id]&&s.group[d.ke].activeMonitors[d.id].watch){
tx({f:'monitor_watch_on',id:d.id,ke:d.ke},'MON_'+d.ke+d.id)
s.tx({viewers:Object.keys(s.group[d.ke].mon[d.id].watch).length,ke:d.ke,id:d.id},'MON_'+d.ke+d.id)
s.tx({viewers:Object.keys(s.group[d.ke].activeMonitors[d.id].watch).length,ke:d.ke,id:d.id},'MON_'+d.ke+d.id)
}
});
break;
@ -1364,20 +1338,9 @@ module.exports = function(s,config,lang,io){
if(s.group[cn.ke].dashcamUsers && s.group[cn.ke].dashcamUsers[cn.auth])delete(s.group[cn.ke].dashcamUsers[cn.auth]);
}
}
if(cn.pluginEngine){
s.connectedPlugins[cn.pluginEngine].plugged = false
s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU')
}
if(cn.cron){
delete(s.cron);
}
if(cn.detectorPlugin){
s.tx({f:'detector_unplugged',plug:cn.detectorPlugin},'CPU')
s.removeDetectorPlugin(cn.detectorPlugin)
s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){
s.tx(data,'CPU')
})
}
if(cn.superSessionKey){
delete(s.superUsersApi[cn.superSessionKey])
}

View file

@ -1,3 +1,5 @@
var fs = require('fs');
var async = require("async");
module.exports = function(s,config){
s.onBeforeDatabaseLoadExtensions.forEach(function(extender){
extender(config)
@ -8,8 +10,8 @@ module.exports = function(s,config){
connection: config.db,
}
if(s.databaseOptions.client.indexOf('sqlite')>-1){
s.databaseOptions.client = 'sqlite3';
s.databaseOptions.useNullAsDefault = true;
s.databaseOptions.client = 'sqlite3'
s.databaseOptions.useNullAsDefault = true
try{
require('sqlite3')
}catch(err){
@ -54,6 +56,11 @@ module.exports = function(s,config){
newValue = new Date(value.replace('T',' '))
return newValue
}
var runQuery = async.queue(function(data, callback) {
s.databaseEngine
.raw(data.query,data.values)
.asCallback(callback)
}, 4);
s.sqlQuery = function(query,values,onMoveOn,hideLog){
if(!values){values=[]}
if(typeof values === 'function'){
@ -66,14 +73,17 @@ module.exports = function(s,config){
// .replace(/ NOT LIKE /g," NOT ILIKE ")
// .replace(/ LIKE /g," ILIKE ")
// }
var mergedQuery = s.mergeQueryValues(query,values)
s.debugLog('s.sqlQuery QUERY',mergedQuery)
if(config.debugLog === true){
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){
return runQuery.push({
query: query,
values: values
},function(err,r){
if(err && !hideLog){
console.log('s.sqlQuery QUERY ERRORED',query)
console.log('s.sqlQuery ERROR',err)
@ -129,6 +139,10 @@ module.exports = function(s,config){
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\',`details` text)' + mySQLtail + ';',[],function(err){
if(err)console.error(err)
},true)
//add Cloud Timelapse Frames table, will remove in future
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Timelapse Frames` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`href` text NOT NULL,`details` longtext,`filename` varchar(50) NOT NULL,`time` timestamp NULL DEFAULT NULL,`size` int(11) NOT NULL)' + mySQLtail + ';',[],function(err){
if(err)console.error(err)
},true)
//create Files table
var createFilesTableQuery = "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',`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP)"
s.sqlQuery(createFilesTableQuery + mySQLtail + ';',[],function(err){
@ -140,7 +154,7 @@ module.exports = function(s,config){
aQuery += "INSERT INTO Files (`ke`, `mid`, `name`, `details`, `size`, `status`, `time`) SELECT `ke`, `mid`, `name`, `details`, `size`, `status`, `time` FROM _Files_old;COMMIT;DROP TABLE _Files_old;"
}else{
s.sqlQuery('ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;',[],function(err){
if(err && err.sqlMessage.indexOf('Duplicate') === -1)console.error(err)
if(err && err.sqlMessage && err.sqlMessage.indexOf('Duplicate') === -1)console.error(err)
},true)
}
},true)

View file

@ -1,12 +1,13 @@
var fs = require('fs');
var request = require('request');
var moment = require('moment');
var crypto = require('crypto');
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
module.exports = function(s,config,lang,io){
console.log('FFmpeg version : '+s.ffmpegVersion)
console.log('Node.js version : '+execSync("node -v"))
console.log('Node.js version : '+process.version)
s.processReady = function(){
s.systemLog(lang.startUpText5)
s.onProcessReadyExtensions.forEach(function(extender){
@ -18,7 +19,7 @@ module.exports = function(s,config,lang,io){
var next = function(){
if(callback)callback()
}
if(!s.isWin){
if(!s.isWin && s.packageJson.mainDirectory !== '.'){
var etcPath = '/etc/shinobisystems/cctv.txt'
fs.stat(etcPath,function(err,stat){
if(err || !stat){
@ -33,6 +34,7 @@ module.exports = function(s,config,lang,io){
}
}
var loadedAccounts = []
var foundMonitors = []
var loadMonitors = function(callback){
s.beforeMonitorsLoadedOnStartupExtensions.forEach(function(extender){
extender()
@ -40,19 +42,17 @@ module.exports = function(s,config,lang,io){
s.systemLog(lang.startUpText4)
//preliminary monitor start
s.sqlQuery('SELECT * FROM Monitors', function(err,monitors) {
foundMonitors = monitors
if(err){s.systemLog(err)}
if(monitors && monitors[0]){
var loadCompleted = 0
var orphanedVideosForMonitors = {}
var loadMonitor = function(monitor){
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
s.initiateMonitorObject(monitor)
s.orphanedVideoCheck(monitor,2,function(orphanedFilesCount){
if(orphanedFilesCount){
orphanedVideosForMonitors[monitor.ke][monitor.mid] += orphanedFilesCount
}
s.group[monitor.ke].mon_conf[monitor.mid] = monitor
setTimeout(function(){
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
s.initiateMonitorObject(monitor)
s.group[monitor.ke].rawMonitorConfigurations[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)
@ -60,10 +60,9 @@ module.exports = function(s,config,lang,io){
if(monitors[loadCompleted]){
loadMonitor(monitors[loadCompleted])
}else{
s.systemLog(lang.startUpText6+' : '+s.s(orphanedVideosForMonitors))
callback()
}
})
},2000)
}
loadMonitor(monitors[loadCompleted])
}else{
@ -71,19 +70,89 @@ module.exports = function(s,config,lang,io){
}
})
}
var checkForOrphanedVideos = function(callback){
var monitors = foundMonitors
if(monitors && monitors[0]){
var loadCompleted = 0
var orphanedVideosForMonitors = {}
var checkForOrphanedVideosForMonitor = function(monitor){
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
s.orphanedVideoCheck(monitor,null,function(orphanedFilesCount){
if(orphanedFilesCount){
orphanedVideosForMonitors[monitor.ke][monitor.mid] += orphanedFilesCount
}
++loadCompleted
if(monitors[loadCompleted]){
checkForOrphanedVideosForMonitor(monitors[loadCompleted])
}else{
s.systemLog(lang.startUpText6+' : '+s.s(orphanedVideosForMonitors))
delete(foundMonitors)
callback()
}
})
}
checkForOrphanedVideosForMonitor(monitors[loadCompleted])
}else{
callback()
}
}
var loadDiskUseForUser = function(user,callback){
s.systemLog(user.mail+' : '+lang.startUpText0)
var userDetails = JSON.parse(user.details)
user.size = 0
user.limit = userDetails.size
s.group[user.ke].sizeLimit = parseFloat(userDetails.size) || 10000
s.group[user.ke].sizeLimitVideoPercent = parseFloat(userDetails.size_video_percent) || 90
s.group[user.ke].sizeLimitTimelapseFramesPercent = parseFloat(userDetails.size_timelapse_percent) || 10
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
if(videos && videos[0]){
videos.forEach(function(video){
user.size += video.size
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE ke=?',[user.ke],function(err,timelapseFrames){
s.sqlQuery('SELECT * FROM `Files` WHERE ke=?',[user.ke],function(err,files){
var usedSpaceVideos = 0
var usedSpaceTimelapseFrames = 0
var usedSpaceFilebin = 0
var addStorageData = {
files: [],
videos: [],
timelapeFrames: [],
}
if(videos && videos[0]){
videos.forEach(function(video){
video.details = s.parseJSON(video.details)
if(!video.details.dir){
usedSpaceVideos += video.size
}else{
addStorageData.videos.push(video)
}
})
}
if(timelapseFrames && timelapseFrames[0]){
timelapseFrames.forEach(function(frame){
frame.details = s.parseJSON(frame.details)
if(!frame.details.dir){
usedSpaceTimelapseFrames += frame.size
}else{
addStorageData.timelapeFrames.push(frame)
}
})
}
if(files && files[0]){
files.forEach(function(file){
file.details = s.parseJSON(file.details)
if(!file.details.dir){
usedSpaceFilebin += file.size
}else{
addStorageData.files.push(file)
}
})
}
s.group[user.ke].usedSpace = (usedSpaceVideos + usedSpaceTimelapseFrames + usedSpaceFilebin) / 1000000
s.group[user.ke].usedSpaceVideos = usedSpaceVideos / 1000000
s.group[user.ke].usedSpaceFilebin = usedSpaceFilebin / 1000000
s.group[user.ke].usedSpaceTimelapseFrames = usedSpaceTimelapseFrames / 1000000
loadAddStorageDiskUseForUser(user,addStorageData,function(){
callback()
})
})
}
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+videos.length,user.size)
callback()
})
})
}
var loadCloudDiskUseForUser = function(user,callback){
@ -98,23 +167,108 @@ module.exports = function(s,config,lang,io){
}
if(s.cloudDiskUseStartupExtensions[storageType])s.cloudDiskUseStartupExtensions[storageType](user,userDetails)
})
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
var loadCloudVideos = function(callback){
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
if(videos && videos[0]){
videos.forEach(function(video){
var storageType = JSON.parse(video.details).type
if(!storageType)storageType = 's3'
var videoSize = video.size / 1000000
user.cloudDiskUse[storageType].usedSpace += videoSize
user.cloudDiskUse[storageType].usedSpaceVideos += videoSize
++user.cloudDiskUse[storageType].firstCount
})
s.cloudDisksLoaded.forEach(function(storageType){
var firstCount = user.cloudDiskUse[storageType].firstCount
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+firstCount,storageType,user.cloudDiskUse[storageType].usedSpace)
delete(user.cloudDiskUse[storageType].firstCount)
})
}
callback()
})
}
var loadCloudTimelapseFrames = function(callback){
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE ke=?',[user.ke],function(err,frames){
if(frames && frames[0]){
frames.forEach(function(frame){
var storageType = JSON.parse(frame.details).type
if(!storageType)storageType = 's3'
var frameSize = frame.size / 1000000
user.cloudDiskUse[storageType].usedSpace += frameSize
user.cloudDiskUse[storageType].usedSpaceTimelapseFrames += frameSize
})
}
callback()
})
}
loadCloudVideos(function(){
loadCloudTimelapseFrames(function(){
s.group[user.ke].cloudDiskUse = user.cloudDiskUse
callback()
})
})
}
var loadAddStorageDiskUseForUser = function(user,data,callback){
var videos = data.videos
var timelapseFrames = data.timelapseFrames
var files = data.files
var userDetails = JSON.parse(user.details)
var userAddStorageData = s.parseJSON(userDetails.addStorage) || {}
var currentStorageNumber = 0
var readStorageArray = function(){
var storage = s.listOfStorage[currentStorageNumber]
if(!storage){
//done all checks, move on to next user
callback()
return
}
var path = storage.value
if(path === ''){
++currentStorageNumber
readStorageArray()
return
}
var storageId = path
var storageData = userAddStorageData[storageId] || {}
if(!s.group[user.ke].addStorageUse[storageId])s.group[user.ke].addStorageUse[storageId] = {}
var storageIndex = s.group[user.ke].addStorageUse[storageId]
storageIndex.name = storage.name
storageIndex.path = path
storageIndex.usedSpace = 0
storageIndex.sizeLimit = parseFloat(storageData.limit) || parseFloat(userDetails.size) || 10000
var usedSpaceVideos = 0
var usedSpaceTimelapseFrames = 0
var usedSpaceFilebin = 0
if(videos && videos[0]){
videos.forEach(function(video){
var storageType = JSON.parse(video.details).type
if(!storageType)storageType = 's3'
user.cloudDiskUse[storageType].usedSpace += (video.size /1000000)
++user.cloudDiskUse[storageType].firstCount
})
s.cloudDisksLoaded.forEach(function(storageType){
var firstCount = user.cloudDiskUse[storageType].firstCount
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+firstCount,storageType,user.cloudDiskUse[storageType].usedSpace)
delete(user.cloudDiskUse[storageType].firstCount)
if(video.details.dir === storage.value){
usedSpaceVideos += video.size
}
})
}
s.group[user.ke].cloudDiskUse = user.cloudDiskUse
callback()
})
if(timelapseFrames && timelapseFrames[0]){
timelapseFrames.forEach(function(frame){
if(video.details.dir === storage.value){
usedSpaceTimelapseFrames += frame.size
}
})
}
if(files && files[0]){
files.forEach(function(file){
if(video.details.dir === storage.value){
usedSpaceFilebin += file.size
}
})
}
storageIndex.usedSpace = (usedSpaceVideos + usedSpaceTimelapseFrames + usedSpaceFilebin) / 1000000
storageIndex.usedSpaceVideos = usedSpaceVideos / 1000000
storageIndex.usedSpaceFilebin = usedSpaceFilebin / 1000000
storageIndex.usedSpaceTimelapseFrames = usedSpaceTimelapseFrames / 1000000
s.systemLog(user.mail+' : '+path+' : '+videos.length,storageIndex.usedSpace)
++currentStorageNumber
readStorageArray()
}
readStorageArray()
}
var loadAdminUsers = function(callback){
//get current disk used for each isolated account (admin user) on startup
@ -124,10 +278,10 @@ module.exports = function(s,config,lang,io){
var count = users.length
var countFinished = 0
users.forEach(function(user){
s.loadGroup(user)
s.loadGroupApps(user)
loadedAccounts.push(user.ke)
loadDiskUseForUser(user,function(){
s.loadGroup(user)
s.loadGroupApps(user)
++countFinished
if(countFinished === count){
callback()
@ -157,36 +311,76 @@ module.exports = function(s,config,lang,io){
}
})
}
config.userHasSubscribed = false
var checkSubscription = function(callback){
var subscriptionFailed = function(){
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
console.error('This Install of Shinobi is NOT Activated')
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
s.systemLog('This Install of Shinobi is NOT Activated')
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
console.log('https://licenses.shinobi.video/subscribe')
}
if(config.subscriptionId){
var url = 'https://licenses.shinobi.video/subscribe/check?subscriptionId=' + config.subscriptionId
request(url,{
method: 'GET',
timeout: 30000
}, function(err,resp,body){
var json = s.parseJSON(body)
if(err)console.log(err,json)
var hasSubcribed = !!json.ok
config.userHasSubscribed = hasSubcribed
callback(hasSubcribed)
if(config.userHasSubscribed){
s.systemLog('This Install of Shinobi is Activated')
}else{
subscriptionFailed()
}
})
}else{
subscriptionFailed()
callback(false)
}
}
//check disk space every 20 minutes
if(config.autoDropCache===true){
setInterval(function(){
exec('echo 3 > /proc/sys/vm/drop_caches',{detached: true})
},60000*20)
}
//master node - startup functions
setInterval(function(){
s.cpuUsage(function(cpu){
s.ramUsage(function(ram){
s.tx({f:'os',cpu:cpu,ram:ram},'CPU');
})
})
},10000)
//hourly check to see if sizePurge has failed to unlock
//checks to see if request count is the number of monitors + 10
s.checkForStalePurgeLocks()
//run prerequsite queries, load users and monitors
if(config.childNodes.mode !== 'child'){
//master node - startup functions
setInterval(function(){
s.cpuUsage(function(cpu){
s.ramUsage(function(ram){
s.tx({f:'os',cpu:cpu,ram:ram},'CPU');
})
})
},10000)
//hourly check to see if sizePurge has failed to unlock
//checks to see if request count is the number of monitors + 10
s.checkForStalePurgeLocks()
//run prerequsite queries, load users and monitors
//sql/database connection with knex
s.databaseEngine = require('knex')(s.databaseOptions)
//run prerequsite queries
s.preQueries()
setTimeout(function(){
checkForTerminalCommands(function(){
//load administrators (groups)
loadAdminUsers(function(){
//load monitors (for groups)
loadMonitors(function(){
s.processReady()
//check for subscription
checkSubscription(function(){
//check terminal commander
checkForTerminalCommands(function(){
//load administrators (groups)
loadAdminUsers(function(){
//load monitors (for groups)
loadMonitors(function(){
//check for orphaned videos
checkForOrphanedVideos(function(){
s.processReady()
})
})
})
})
})

368
libs/timelapse.js Normal file
View file

@ -0,0 +1,368 @@
var fs = require('fs')
var moment = require('moment')
var express = require('express')
module.exports = function(s,config,lang,app,io){
s.getTimelapseFrameDirectory = function(e){
if(e.mid&&!e.id){e.id=e.mid}
s.checkDetails(e)
if(e.details&&e.details.dir&&e.details.dir!==''){
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'_timelapse/'
}else{
return s.dir.videos+e.ke+'/'+e.id+'_timelapse/';
}
}
s.createTimelapseFrameAndInsert = function(e,location,filename){
//e = monitor object
//location = file location
var filePath = location + filename
var fileStats = fs.statSync(filePath)
var details = {}
if(e.details && e.details.dir && e.details.dir !== ''){
details.dir = e.details.dir
}
var timeNow = new Date()
var queryInfo = {
ke: e.ke,
mid: e.id,
details: s.s(details),
filename: filename,
size: fileStats.size,
time: timeNow
}
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
var currentDate = s.formattedTime(queryInfo.time,'YYYY-MM-DD')
s.cx({
f: 'open_timelapse_file_transfer',
ke: e.ke,
mid: e.id,
d: s.group[e.ke].rawMonitorConfigurations[e.id],
filename: filename,
currentDate: currentDate,
queryInfo: queryInfo
})
var formattedTime = s.timeObject(timeNow).format()
fs.createReadStream(filePath,{ highWaterMark: 500 })
.on('data',function(data){
s.cx({
f: 'created_timelapse_file_chunk',
ke: e.ke,
mid: e.id,
time: formattedTime,
filesize: e.filesize,
chunk: data,
d: s.group[e.ke].rawMonitorConfigurations[e.id],
filename: filename,
currentDate: currentDate,
queryInfo: queryInfo
})
})
.on('close',function(){
s.cx({
f: 'created_timelapse_file',
ke: e.ke,
mid: e.id,
time: formattedTime,
filesize: e.filesize,
d: s.group[e.ke].rawMonitorConfigurations[e.id],
filename: filename,
currentDate: currentDate,
queryInfo: queryInfo
})
})
}else{
s.insertTimelapseFrameDatabaseRow(e,queryInfo,filePath)
}
}
s.insertTimelapseFrameDatabaseRow = function(e,queryInfo,filePath){
s.sqlQuery('INSERT INTO `Timelapse Frames` ('+Object.keys(queryInfo).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(queryInfo))
s.setDiskUsedForGroup(e,queryInfo.size / 1000000,'timelapeFrames')
s.purgeDiskForGroup(e)
s.onInsertTimelapseFrameExtensions.forEach(function(extender){
extender(e,queryInfo,filePath)
})
}
s.onDeleteTimelapseFrameFromCloudExtensions = {}
s.onDeleteTimelapseFrameFromCloudExtensionsRunner = function(e,storageType,video){
// e = user
if(!storageType){
var videoDetails = JSON.parse(r.details)
videoDetails.type = videoDetails.type || 's3'
}
if(s.onDeleteTimelapseFrameFromCloudExtensions[storageType]){
s.onDeleteTimelapseFrameFromCloudExtensions[storageType](e,video,function(){
s.tx({
f: 'timelapse_frame_delete_cloud',
mid: e.mid,
ke: e.ke,
time: e.time,
end: e.end
},'GRP_'+e.ke);
})
}
}
s.deleteTimelapseFrameFromCloud = function(e){
// e = video object
s.checkDetails(e)
var frameSelector = [e.id,e.ke,new Date(e.time)]
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(err,r){
if(r&&r[0]){
r = r[0]
s.sqlQuery('DELETE FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(){
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,r)
})
}else{
// console.log('Delete Failed',e)
// console.error(err)
}
})
}
// Web Paths
// // // // //
/**
* API : Get Timelapse images
*/
app.get([
config.webPaths.apiPrefix+':auth/timelapse/:ke',
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id',
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date',
], function (req,res){
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
if(
user.permissions.watch_videos==="0" ||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
){
res.end(s.prettyPrint([]))
return
}
req.sql='SELECT * FROM `Timelapse Frames` WHERE ke=?';req.ar=[req.params.ke];
if(req.query.archived=='1'){
req.sql+=' AND details LIKE \'%"archived":"1"\''
}
if(!req.params.id){
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
req.or=[];
user.details.monitors.forEach(function(v,n){
req.or.push('mid=?');req.ar.push(v)
})
req.sql+=' AND ('+req.or.join(' OR ')+')'
}
}else{
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
req.sql+=' and mid=?'
req.ar.push(req.params.id)
}else{
res.end('[]');
return;
}
}
var isMp4Call = false
if(req.query.mp4){
isMp4Call = true
}
if(req.params.date){
if(req.params.date.indexOf('-') === -1 && !isNaN(req.params.date)){
req.params.date = parseInt(req.params.date)
}
var selectedDate = req.params.date
if(typeof req.params.date === 'string' && req.params.date.indexOf('.') > -1){
isMp4Call = true
selectedDate = req.params.date.split('.')[0]
}
selectedDate = new Date(selectedDate)
var utcSelectedDate = new Date(selectedDate.getTime() + selectedDate.getTimezoneOffset() * 60000)
req.query.start = moment(utcSelectedDate).format('YYYY-MM-DD HH:mm:ss')
var dayAfter = utcSelectedDate
dayAfter.setDate(dayAfter.getDate() + 1)
req.query.end = moment(dayAfter).format('YYYY-MM-DD HH:mm:ss')
}
if(req.query.start||req.query.end){
if(!req.query.startOperator||req.query.startOperator==''){
req.query.startOperator='>='
}
if(!req.query.endOperator||req.query.endOperator==''){
req.query.endOperator='<='
}
if(req.query.start && req.query.start !== '' && req.query.end && req.query.end !== ''){
req.query.start = s.stringToSqlTime(req.query.start)
req.query.end = s.stringToSqlTime(req.query.end)
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `time` '+req.query.endOperator+' ?';
req.ar.push(req.query.start)
req.ar.push(req.query.end)
}else if(req.query.start && req.query.start !== ''){
req.query.start = s.stringToSqlTime(req.query.start)
req.sql+=' AND `time` '+req.query.startOperator+' ?';
req.ar.push(req.query.start)
}
}
// if(!req.query.limit||req.query.limit==''){req.query.limit=288}
req.sql+=' ORDER BY `time` DESC'
s.sqlQuery(req.sql,req.ar,function(err,r){
if(isMp4Call){
if(r && r[0]){
s.createVideoFromTimelapse(r,req.query.fps,function(response){
if(response.fileExists){
if(req.query.download){
res.setHeader('Content-Type', 'video/mp4')
s.streamMp4FileOverHttp(response.fileLocation,req,res)
}else{
res.setHeader('Content-Type', 'application/json')
res.end(s.prettyPrint({
ok : response.ok,
fileExists : response.fileExists,
msg : response.msg,
}))
}
}else{
res.setHeader('Content-Type', 'application/json')
res.end(s.prettyPrint({
ok : response.ok,
fileExists : response.fileExists,
msg : response.msg,
}))
}
})
}else{
res.setHeader('Content-Type', 'application/json');
res.end(s.prettyPrint([]))
}
}else{
if(r && r[0]){
r.forEach(function(file){
file.details = s.parseJSON(file.details)
})
res.end(s.prettyPrint(r))
}else{
res.end(s.prettyPrint([]))
}
}
})
},res,req);
});
/**
* API : Get Timelapse images
*/
app.get([
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date/:filename',
], function (req,res){
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
if(
user.permissions.watch_videos==="0" ||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
){
res.end(s.prettyPrint([]))
return
}
req.sql='SELECT * FROM `Timelapse Frames` WHERE ke=?';req.ar=[req.params.ke];
if(req.query.archived=='1'){
req.sql+=' AND details LIKE \'%"archived":"1"\''
}
if(!req.params.id){
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
req.or=[];
user.details.monitors.forEach(function(v,n){
req.or.push('mid=?');req.ar.push(v)
})
req.sql+=' AND ('+req.or.join(' OR ')+')'
}
}else{
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
req.sql+=' and mid=?'
req.ar.push(req.params.id)
}else{
res.end('[]');
return;
}
}
req.sql+=' AND filename=?'
req.ar.push(req.params.filename)
req.sql+=' ORDER BY `time` DESC'
s.sqlQuery(req.sql,req.ar,function(err,r){
if(r && r[0]){
var frame = r[0]
frame.details = s.parseJSON(frame.details)
var fileLocation
if(frame.details.dir){
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
}else{
fileLocation = `${s.dir.videos}`
}
var selectedDate = req.params.date
if(selectedDate.indexOf('-') === -1){
selectedDate = req.params.filename.split('T')[0]
}
fileLocation = `${fileLocation}${frame.ke}/${frame.mid}_timelapse/${selectedDate}/${req.params.filename}`
fs.stat(fileLocation,function(err,stats){
if(!err){
res.contentType('image/jpeg')
res.on('finish',function(){res.end()})
fs.createReadStream(fileLocation).pipe(res)
}else{
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
}
})
}else{
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
}
})
},res,req);
});
/**
* Page : Get Timelapse Page (Not Modal)
*/
app.get(config.webPaths.apiPrefix+':auth/timelapsePage/:ke', function (req,res){
req.params.protocol=req.protocol;
s.auth(req.params,function(user){
// if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
// res.end(user.lang['Not Permitted'])
// return
// }
req.params.uid = user.uid
s.renderPage(req,res,config.renderPaths.timelapse,{
$user: user,
data: req.params,
config: s.getConfigWithBranding(req.hostname),
lang: user.lang,
originalURL: s.getOriginalUrl(req)
})
},res,req);
});
var buildTimelapseVideos = function(){
var dateNow = new Date()
var hoursNow = dateNow.getHours()
if(hoursNow === 1){
var dateNowMoment = moment(dateNow).utc().format('YYYY-MM-DDTHH:mm:ss')
var dateMinusOneDay = moment(dateNow).utc().subtract(1, 'days').format('YYYY-MM-DDTHH:mm:ss')
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE time => ? AND time =< ?',[dateMinusOneDay,dateNowMoment],function(err,frames){
console.log(frames.length)
var groups = {}
frames.forEach(function(frame){
if(groups[frame.ke])groups[frame.ke] = {}
if(groups[frame.ke][frame.mid])groups[frame.ke][frame.mid] = []
groups[frame.ke][frame.mid].push(frame)
})
Object.keys(groups).forEach(function(groupKey){
Object.keys(groups[groupKey]).forEach(function(monitorId){
var frameSet = groups[groupKey][monitorId]
s.createVideoFromTimelapse(frameSet,30,function(response){
if(response.ok){
}
console.log(response.fileLocation)
})
})
})
})
}
}
// Auto Build Timelapse Videos
if(config.autoBuildTimelapseVideosDaily === true){
setInterval(buildTimelapseVideos,1000 * 60 * 60 * 0.75)//every 45 minutes
buildTimelapseVideos()
}
}

View file

@ -1,20 +1,21 @@
module.exports = function(s,config,lang){
config.uploaderEjsBlocks = []
s.uploaderFields = []
var loadLib = function(lib){
var uploadersFolder = __dirname + '/uploaders/'
var libraryPath = uploadersFolder + lib + '.js'
var loadedLib = require(libraryPath)(s,config,lang)
if(lib !== 'loader'){
var libraryEjsFile = uploadersFolder + lib + '.ejs'
config.uploaderEjsBlocks.push(libraryEjsFile)
loadedLib.isFormGroupGroup = true
s.uploaderFields.push(loadedLib)
}
return require(libraryPath)
return loadedLib
}
loadLib('loader')(s,config,lang)
loadLib('loader')
//cloud storage
loadLib('s3based')(s,config,lang)
loadLib('backblazeB2')(s,config,lang)
loadLib('amazonS3')(s,config,lang)
loadLib('webdav')(s,config,lang)
loadLib('s3based')
loadLib('backblazeB2')
loadLib('amazonS3')
loadLib('webdav')
//simple storage
loadLib('sftp')(s,config,lang)
loadLib('sftp')
}

View file

@ -123,6 +123,67 @@ module.exports = function(s,config,lang){
})
}
}
var onInsertTimelapseFrame = function(monitorObject,queryInfo,filePath){
var e = monitorObject
if(s.group[e.ke].aws_s3 && s.group[e.ke].init.use_aws_s3 !== '0' && s.group[e.ke].init.aws_s3_save === '1'){
var fileStream = fs.createReadStream(filePath)
fileStream.on('error', function (err) {
console.error(err)
})
var saveLocation = s.group[e.ke].init.aws_s3_dir + e.ke + '/' + e.mid + '_timelapse/' + queryInfo.filename
s.group[e.ke].aws_s3.upload({
Bucket: s.group[e.ke].init.aws_s3_bucket,
Key: saveLocation,
Body: fileStream,
ACL:'public-read',
ContentType:'image/jpeg'
},function(err,data){
if(err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
}
if(s.group[e.ke].init.aws_s3_log === '1' && data && data.Location){
var save = [
queryInfo.mid,
queryInfo.ke,
queryInfo.time,
s.s({
type : 's3',
location : saveLocation,
}),
queryInfo.size,
data.Location
]
s.sqlQuery('INSERT INTO `Cloud Timelapse Frames` (mid,ke,time,details,size,href) VALUES (?,?,?,?,?,?)',save)
s.setCloudDiskUsedForGroup(e,{
amount : s.kilobyteToMegabyte(queryInfo.size),
storageType : 's3'
},'timelapseFrames')
s.purgeCloudDiskForGroup(e,'s3','timelapseFrames')
}
})
}
}
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
// e = user
try{
var frameDetails = JSON.parse(frame.details)
}catch(err){
var frameDetails = frame.details
}
if(frameDetails.type !== 's3'){
return
}
if(!frameDetails.location){
frameDetails.location = frame.href.split(locationUrl)[1]
}
s.group[e.ke].aws_s3.deleteObject({
Bucket: s.group[e.ke].init.aws_s3_bucket,
Key: frameDetails.location,
}, function(err, data) {
if (err) console.log(err);
callback()
});
}
//amazon s3
s.addCloudUploader({
name: 's3',
@ -133,5 +194,215 @@ module.exports = function(s,config,lang){
cloudDiskUseStartupExtensions: cloudDiskUseStartupForAmazonS3,
beforeAccountSave: beforeAccountSaveForAmazonS3,
onAccountSave: cloudDiskUseStartupForAmazonS3,
onInsertTimelapseFrame: onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud
})
}
//return fields that will appear in settings
return {
"evaluation": "details.use_aws_s3 !== '0'",
"name": lang["Amazon S3"],
"color": "forestgreen",
"info": [
{
"name": "detail=aws_s3_save",
"selector":"autosave_aws_s3",
"field": lang.Autosave,
"description": "",
"default": lang.No,
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"field": lang.Bucket,
"name": "detail=aws_s3_bucket",
"placeholder": "Example : slippery-seal",
"form-group-class": "autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.aws_accessKeyId,
"name": "detail=aws_accessKeyId",
"form-group-class": "autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=aws_secretAccessKey",
"fieldType":"password",
"placeholder": "",
"field": lang.aws_secretAccessKey,
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=aws_region",
"field": lang.Region,
"fieldType": "select",
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": "US West (N. California)",
"value": "us-west-1"
},
{
"name": "US West (Oregon)",
"value": "us-west-2"
},
{
"name": "US East (Ohio)",
"value": "us-east-2"
},
{
"name": "US East (N. Virginia)",
"value": "us-east-1"
},
{
"name": "Asia Pacific (Mumbai)",
"value": "ap-south-1"
},
{
"name": "Asia Pacific (Seoul)",
"value": "ap-northeast-2"
},
{
"name": "Asia Pacific (Osaka-Local)**",
"value": "ap-northeast-3"
},
{
"name": "Asia Pacific (Singapore)",
"value": "ap-southeast-1"
},
{
"name": "Asia Pacific (Sydney)",
"value": "ap-southeast-2"
},
{
"name": "Asia Pacific (Tokyo)",
"value": "ap-northeast-1"
},
{
"name": "Canada (Central)",
"value": "ca-central-1"
},
{
"name": "China (Beijing)",
"value": "cn-north-1"
},
{
"name": "China (Ningxia)",
"value": "cn-northwest-1"
},
{
"name": "EU (Frankfurt)",
"value": "eu-central-1"
},
{
"name": "EU (Ireland)",
"value": "eu-west-1"
},
{
"name": "EU (London)",
"value": "eu-west-2"
},
{
"name": "EU (Paris)",
"value": "eu-west-3"
},
{
"name": "South America (São Paulo)",
"value": "sa-east-1"
}
]
},
{
"hidden": true,
"name": "detail=aws_s3_log",
"field": lang['Save Links to Database'],
"fieldType": "select",
"selector": "h_s3sld",
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=use_aws_s3_size_limit",
"field": lang['Use Max Storage Amount'],
"fieldType": "select",
"selector": "h_s3zl",
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"form-group-class-pre-layer":"h_s3sld_input h_s3sld_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=aws_s3_size_limit",
"field": lang['Max Storage Amount'],
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"form-group-class-pre-layer":"h_s3sld_input h_s3sld_1",
"description": "",
"default": "10000",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=aws_s3_dir",
"field": lang['Save Directory'],
"form-group-class":"autosave_aws_s3_input autosave_aws_s3_1",
"description": "",
"default": "/",
"example": "",
"possible": ""
},
]
}
}

View file

@ -167,4 +167,127 @@ module.exports = function(s,config,lang){
beforeAccountSave: beforeAccountSaveForBackblazeB2,
onAccountSave: cloudDiskUseStartupForBackblazeB2,
})
return {
"evaluation": "details.use_bb_b2 !== '0'",
"name": lang["Backblaze B2"],
"color": "forestgreen",
"info": [
{
"name": "detail=bb_b2_save",
"selector":"autosave_bb_b2",
"field": lang.Autosave,
"description": "",
"default": lang.No,
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"field": lang.Bucket,
"name": "detail=bb_b2_bucket",
"placeholder": "Example : slippery-seal",
"form-group-class": "autosave_bb_b2_input autosave_bb_b2_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.keyId,
"name": "detail=bb_b2_accountId",
"form-group-class": "autosave_bb_b2_input autosave_bb_b2_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=bb_b2_applicationKey",
"fieldType":"password",
"placeholder": "XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXX",
"field": lang.applicationKey,
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=bb_b2_log",
"field": lang['Save Links to Database'],
"fieldType": "select",
"selector": "h_b2sld",
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=use_bb_b2_size_limit",
"field": lang['Use Max Storage Amount'],
"fieldType": "select",
"selector": "h_b2zl",
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
"form-group-class-pre-layer":"h_b2sld_input h_b2sld_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=bb_b2_size_limit",
"field": lang['Max Storage Amount'],
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
"form-group-class-pre-layer":"h_b2sld_input h_b2sld_1",
"description": "",
"default": "10000",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=bb_b2_dir",
"field": lang['Save Directory'],
"form-group-class":"autosave_bb_b2_input autosave_bb_b2_1",
"description": "",
"default": "/",
"example": "",
"possible": ""
},
]
}
}

View file

@ -5,6 +5,8 @@ module.exports = function(s){
s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender)
s.deleteVideoFromCloudExtensions[opt.name] = opt.deleteVideoFromCloudExtensions
s.cloudDiskUseStartupExtensions[opt.name] = opt.cloudDiskUseStartupExtensions
if(opt.onInsertTimelapseFrame)s.onInsertTimelapseFrame(opt.onInsertTimelapseFrame)
if(opt.onDeleteTimelapseFrameFromCloud)s.onDeleteTimelapseFrameFromCloudExtensions[opt.name] = opt.onDeleteTimelapseFrameFromCloud
s.beforeAccountSave(opt.beforeAccountSave)
s.onAccountSave(opt.onAccountSave)
s.cloudDisksLoader(opt.name)
@ -13,6 +15,7 @@ module.exports = function(s){
s.loadGroupAppExtender(opt.loadGroupAppExtender)
s.unloadGroupAppExtender(opt.unloadGroupAppExtender)
s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender)
if(opt.onInsertTimelapseFrame)s.onInsertTimelapseFrame(opt.onInsertTimelapseFrame)
s.beforeAccountSave(opt.beforeAccountSave)
s.onAccountSave(opt.onAccountSave)
s.onMonitorSave(opt.onMonitorSave)

View file

@ -34,8 +34,6 @@ module.exports = function(s,config,lang){
userDetails.whcs_accessKeyId !== ''&&
userDetails.whcs_secretAccessKey &&
userDetails.whcs_secretAccessKey !== ''&&
userDetails.whcs_region &&
userDetails.whcs_region !== ''&&
userDetails.whcs_bucket !== ''
){
if(!userDetails.whcs_dir || userDetails.whcs_dir === '/'){
@ -50,6 +48,10 @@ module.exports = function(s,config,lang){
if(!userDetails.whcs_endpoint || userDetails.whcs_endpoint === ''){
userDetails.whcs_endpoint = 's3.wasabisys.com'
}
var whcs_region = null
if(userDetails.whcs_region && userDetails.whcs_region !== ''){
whcs_region = userDetails.whcs_region
}
var endpointSplit = userDetails.whcs_endpoint.split('.')
if(endpointSplit.length > 2){
endpointSplit.shift()
@ -62,7 +64,7 @@ module.exports = function(s,config,lang){
endpoint: wasabiEndpoint,
accessKeyId: userDetails.whcs_accessKeyId,
secretAccessKey: userDetails.whcs_secretAccessKey,
region: userDetails.whcs_region
region: whcs_region
})
s.group[e.ke].whcs = new s.group[e.ke].whcs.S3();
}
@ -100,9 +102,10 @@ module.exports = function(s,config,lang){
fileStream.on('error', function (err) {
console.error(err)
})
var bucketName = s.group[e.ke].init.whcs_bucket
var saveLocation = s.group[e.ke].init.whcs_dir+e.ke+'/'+e.mid+'/'+k.filename
s.group[e.ke].whcs.upload({
Bucket: s.group[e.ke].init.whcs_bucket,
Bucket: bucketName,
Key: saveLocation,
Body:fileStream,
ACL:'public-read',
@ -112,6 +115,8 @@ module.exports = function(s,config,lang){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
}
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
var cloudLink = data.Location
cloudLink = fixCloudianUrl(e,cloudLink)
var save = [
e.mid,
e.ke,
@ -123,7 +128,7 @@ module.exports = function(s,config,lang){
}),
k.filesize,
k.endTime,
data.Location
cloudLink
]
s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save)
s.setCloudDiskUsedForGroup(e,{
@ -135,6 +140,83 @@ module.exports = function(s,config,lang){
})
}
}
var onInsertTimelapseFrame = function(monitorObject,queryInfo,filePath){
var e = monitorObject
if(s.group[e.ke].whcs && s.group[e.ke].init.use_whcs !== '0' && s.group[e.ke].init.whcs_save === '1'){
var fileStream = fs.createReadStream(filePath)
fileStream.on('error', function (err) {
console.error(err)
})
var saveLocation = s.group[e.ke].init.whcs_dir + e.ke + '/' + e.mid + '_timelapse/' + queryInfo.filename
s.group[e.ke].whcs.upload({
Bucket: s.group[e.ke].init.whcs_bucket,
Key: saveLocation,
Body: fileStream,
ACL:'public-read',
ContentType:'image/jpeg'
},function(err,data){
if(err){
s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err})
}
if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){
var save = [
queryInfo.mid,
queryInfo.ke,
queryInfo.time,
s.s({
type : 'whcs',
location : saveLocation,
}),
queryInfo.size,
data.Location
]
s.sqlQuery('INSERT INTO `Cloud Timelapse Frames` (mid,ke,time,details,size,href) VALUES (?,?,?,?,?,?)',save)
s.setCloudDiskUsedForGroup(e,{
amount : s.kilobyteToMegabyte(queryInfo.size),
storageType : 'whcs'
},'timelapseFrames')
s.purgeCloudDiskForGroup(e,'whcs','timelapseFrames')
}
})
}
}
var onDeleteTimelapseFrameFromCloud = function(e,frame,callback){
// e = user
try{
var frameDetails = JSON.parse(frame.details)
}catch(err){
var frameDetails = frame.details
}
if(frameDetails.type !== 'whcs'){
return
}
if(!frameDetails.location){
frameDetails.location = frame.href.split(locationUrl)[1]
}
s.group[e.ke].whcs.deleteObject({
Bucket: s.group[e.ke].init.whcs_bucket,
Key: frameDetails.location,
}, function(err, data) {
if (err) console.log(err);
callback()
});
}
var fixCloudianUrl = function(e,cloudLink){
if(cloudLink.indexOf('http') === -1){
var bucketName = s.group[e.ke].init.whcs_bucket
var endPointSplit = s.group[e.ke].init.whcs_endpoint.split('://')
endPoint = endPointSplit[1] || endPointSplit[0]
var protocol = `https`
if(endPointSplit[1])protocol = endPointSplit[0]
var cloudLinkPrefix = `${protocol}://${bucketName}.${endPoint}`
var truncatedLink = cloudLink.substring(0, bucketName.length + 3)
if(truncatedLink.indexOf(`${bucketName}/`) > -1){
cloudLink = cloudLink.replace(`${bucketName}/`,'')
}
cloudLink = s.checkCorrectPathEnding(cloudLinkPrefix) + cloudLink
}
return cloudLink
}
//wasabi
s.addCloudUploader({
name: 'whcs',
@ -145,5 +227,250 @@ module.exports = function(s,config,lang){
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWasabiHotCloudStorage,
beforeAccountSave: beforeAccountSaveForWasabiHotCloudStorage,
onAccountSave: cloudDiskUseStartupForWasabiHotCloudStorage,
onInsertTimelapseFrame: onInsertTimelapseFrame,
onDeleteTimelapseFrameFromCloud: onDeleteTimelapseFrameFromCloud
})
return {
"evaluation": "details.use_whcs !== '0'",
"name": lang["S3-Based Network Storage"],
"color": "forestgreen",
"info": [
{
"name": "detail=whcs_save",
"selector":"autosave_whcs",
"field": lang.Autosave,
"description": "",
"default": lang.No,
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=use_whcs_endpoint_select",
"selector":"h_whcs_endpoint",
"field": lang.Endpoint,
"description": "",
"default": "",
"example": "",
"fieldType": "select",
"possible": [
{
"name": "Custom Endpoint",
"value": ""
},
{
"name": lang['Wasabi Hot Cloud Storage'],
"value": "s3.wasabisys.com"
}
]
},
{
"hidden": true,
"field": lang['Endpoint Address'],
"name": "detail=whcs_endpoint",
"placeholder": "s3.wasabisys.com",
"form-group-class": "autosave_whcs_input autosave_whcs_1",
"form-group-class-pre-layer":"h_whcs_endpoint_input h_whcs_endpoint_",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Bucket,
"name": "detail=whcs_bucket",
"placeholder": "Example : slippery-seal",
"form-group-class": "autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.aws_accessKeyId,
"name": "detail=whcs_accessKeyId",
"form-group-class": "autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=whcs_secretAccessKey",
"fieldType":"password",
"placeholder": "",
"field": lang.aws_secretAccessKey,
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=whcs_region",
"field": lang.Region,
"fieldType": "select",
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang['No Region'],
"value": ""
},
{
"name": "US West 1",
"value": "us-west-1"
},
{
"name": "US West 2)",
"value": "us-west-2"
},
{
"name": "US East 1",
"value": "us-east-2"
},
{
"name": "US East 2",
"value": "us-east-1"
},
{
"name": "Asia Pacific 1",
"value": "ap-south-1"
},
{
"name": "Asia Pacific 2",
"value": "ap-northeast-2"
},
{
"name": "Asia Pacific 3",
"value": "ap-northeast-3"
},
{
"name": "Asia Pacific 4",
"value": "ap-southeast-1"
},
{
"name": "Asia Pacific 5",
"value": "ap-southeast-2"
},
{
"name": "Asia Pacific 6",
"value": "ap-northeast-1"
},
{
"name": "Canada 1",
"value": "ca-central-1"
},
{
"name": "China 1",
"value": "cn-north-1"
},
{
"name": "China 1",
"value": "cn-northwest-1"
},
{
"name": "EU 1",
"value": "eu-central-1"
},
{
"name": "EU 2",
"value": "eu-west-1"
},
{
"name": "EU 3",
"value": "eu-west-2"
},
{
"name": "EU 4",
"value": "eu-west-3"
},
{
"name": "South America 1",
"value": "sa-east-1"
}
]
},
{
"hidden": true,
"name": "detail=whcs_log",
"field": lang['Save Links to Database'],
"fieldType": "select",
"selector": "h_whcssld",
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=use_whcs_size_limit",
"field": lang['Use Max Storage Amount'],
"fieldType": "select",
"selector": "h_whcszl",
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"form-group-class-pre-layer":"h_whcssld_input h_whcssld_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=whcs_size_limit",
"field": lang['Max Storage Amount'],
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"form-group-class-pre-layer":"h_whcssld_input h_whcssld_1",
"description": "",
"default": "10000",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=whcs_dir",
"field": lang['Save Directory'],
"form-group-class":"autosave_whcs_input autosave_whcs_1",
"description": "",
"default": "/",
"example": "",
"possible": ""
},
]
}
}

View file

@ -72,8 +72,8 @@ module.exports = function(s,config,lang){
}
var onAccountSaveForSftp = function(group,userDetails,user){
if(s.group[user.ke] && s.group[user.ke].sftp && s.group[user.ke].init.use_sftp !== '0' && s.group[user.ke].init.sftp_save === '1'){
Object.keys(s.group[user.ke].mon_conf).forEach(function(monitorId){
createSftpDirectory(s.group[user.ke].mon_conf[monitorId])
Object.keys(s.group[user.ke].rawMonitorConfigurations).forEach(function(monitorId){
createSftpDirectory(s.group[user.ke].rawMonitorConfigurations[monitorId])
})
}
}
@ -87,4 +87,92 @@ module.exports = function(s,config,lang){
onAccountSave: onAccountSaveForSftp,
onMonitorSave: onMonitorSaveForSftp,
})
return {
"evaluation": "details.use_sftp !== '0'",
"name": lang['SFTP (SSH File Transfer)'],
"color": "forestgreen",
"info": [
{
"name": "detail=sftp_save",
"selector":"autosave_sftp",
"field": lang.Autosave,
"description": "",
"default": lang.No,
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"field": lang.Host,
"name": "detail=sftp_host",
"form-group-class": "autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Port,
"name": "detail=sftp_port",
"form-group-class": "autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Username,
"name": "detail=sftp_username",
"form-group-class": "autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Password,
"fieldType": "password",
"name": "detail=sftp_password",
"form-group-class": "autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.privateKey,
"fieldType": "textarea",
"name": "detail=sftp_privateKey",
"form-group-class": "autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=sftp_dir",
"field": lang['Save Directory'],
"form-group-class":"autosave_sftp_input autosave_sftp_1",
"description": "",
"default": "/",
"example": "",
"possible": ""
},
]
}
}

View file

@ -76,7 +76,7 @@ module.exports = function(s,config,lang){
if(wfs && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === "1"){
var webdavUploadDir = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/'
var startWebDavUpload = function(){
s.group[e.ke].mon[e.id].webdavDirExist = true
s.group[e.ke].activeMonitors[e.id].webdavDirExist = true
var wfsWriteStream =
fs.createReadStream(k.dir + k.filename).pipe(wfs.createWriteStream(webdavUploadDir + k.filename))
if(s.group[e.ke].init.webdav_log === '1'){
@ -102,7 +102,7 @@ module.exports = function(s,config,lang){
s.purgeCloudDiskForGroup(e,'webdav')
}
}
if(s.group[e.ke].mon[e.id].webdavDirExist !== true){
if(s.group[e.ke].activeMonitors[e.id].webdavDirExist !== true){
//check if webdav dir exist
var parentPoint = 0
var webDavParentz = webdavUploadDir.split('/')
@ -166,4 +166,125 @@ module.exports = function(s,config,lang){
beforeAccountSave: beforeAccountSaveForWebDav,
onAccountSave: cloudDiskUseStartupForWebDav,
})
return {
"evaluation": "details.use_webdav !== '0'",
"name": lang.WebDAV,
"color": "forestgreen",
"info": [
{
"name": "detail=webdav_save",
"selector":"autosave_webdav",
"field": lang.Autosave,
"description": "",
"default": lang.No,
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"field": lang.URL,
"name": "detail=webdav_url",
"form-group-class": "autosave_webdav_input autosave_webdav_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Username,
"name": "detail=webdav_user",
"form-group-class": "autosave_webdav_input autosave_webdav_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"field": lang.Password,
"fieldType": "password",
"name": "detail=webdav_pass",
"form-group-class": "autosave_webdav_input autosave_webdav_1",
"description": "",
"default": "",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=webdav_log",
"field": lang['Save Links to Database'],
"fieldType": "select",
"selector": "h_webdavsld",
"form-group-class":"autosave_webdav_input autosave_webdav_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=use_webdav_size_limit",
"field": lang['Use Max Storage Amount'],
"fieldType": "select",
"selector": "h_webdavzl",
"form-group-class":"autosave_webdav_input autosave_webdav_1",
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
"description": "",
"default": "",
"example": "",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
{
"hidden": true,
"name": "detail=webdav_size_limit",
"field": lang['Max Storage Amount'],
"form-group-class":"autosave_webdav_input autosave_webdav_1",
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
"description": "",
"default": "10000",
"example": "",
"possible": ""
},
{
"hidden": true,
"name": "detail=webdav_dir",
"field": lang['Save Directory'],
"form-group-class":"autosave_webdav_input autosave_webdav_1",
"description": "",
"default": "/",
"example": "",
"possible": ""
},
]
}
}

View file

@ -2,80 +2,262 @@ var fs = require('fs');
var events = require('events');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
module.exports = function(s,config){
module.exports = function(s,config,lang){
s.purgeDiskForGroup = function(e){
if(config.cron.deleteOverMax === true){
s.group[e.ke].sizePurgeQueue.push(1)
if(s.group[e.ke].sizePurging !== true){
s.group[e.ke].sizePurging = true
var finish = function(){
//remove value just used from queue
s.group[e.ke].sizePurgeQueue.shift()
//do next one
if(s.group[e.ke].sizePurgeQueue.length > 0){
checkQueue()
if(config.cron.deleteOverMax === true && s.group[e.ke] && s.group[e.ke].sizePurgeQueue){
s.group[e.ke].sizePurgeQueue.push(1)
if(s.group[e.ke].sizePurging !== true){
s.group[e.ke].sizePurging = true
var finish = function(){
//remove value just used from queue
s.group[e.ke].sizePurgeQueue.shift()
//do next one
if(s.group[e.ke].sizePurgeQueue.length > 0){
checkQueue()
}else{
s.group[e.ke].sizePurging = false
s.sendDiskUsedAmountToClients(e)
}
}
var checkQueue = function(){
//get first in queue
var currentPurge = s.group[e.ke].sizePurgeQueue[0]
var reRunCheck = function(){}
var deleteSetOfVideos = function(err,videos,storageIndex,callback){
var videosToDelete = []
var queryValues = [e.ke]
var completedCheck = 0
if(videos){
videos.forEach(function(video){
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
videosToDelete.push('(mid=? AND `time`=?)')
queryValues.push(video.mid)
queryValues.push(video.time)
fs.chmod(video.dir,0o777,function(err){
fs.unlink(video.dir,function(err){
++completedCheck
if(err){
fs.stat(video.dir,function(err){
if(!err){
s.file('delete',video.dir)
}
})
}
if(videosToDelete.length === completedCheck){
videosToDelete = videosToDelete.join(' OR ')
s.sqlQuery('DELETE FROM Videos WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
reRunCheck()
})
}
})
})
if(storageIndex){
s.setDiskUsedForGroupAddStorage(e,{
size: -(video.size/1000000),
storageIndex: storageIndex
})
}else{
s.setDiskUsedForGroup(e,-(video.size/1000000))
}
s.tx({
f: 'video_delete',
ff: 'over_max',
filename: s.formattedTime(video.time)+'.'+video.ext,
mid: video.mid,
ke: video.ke,
time: video.time,
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
},'GRP_'+e.ke)
})
}else{
s.group[e.ke].sizePurging = false
s.sendDiskUsedAmountToClients(e)
console.log(err)
}
if(videosToDelete.length === 0){
if(callback)callback()
}
}
var checkQueue = function(){
//get first in queue
var currentPurge = s.group[e.ke].sizePurgeQueue[0]
var deleteVideos = function(){
//run purge command
if(s.group[e.ke].usedSpace > (s.group[e.ke].sizeLimit*config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,videos){
var videosToDelete = []
var queryValues = [e.ke]
var completedCheck = 0
if(videos){
videos.forEach(function(video){
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
videosToDelete.push('(mid=? AND `time`=?)')
queryValues.push(video.mid)
queryValues.push(video.time)
fs.chmod(video.dir,0o777,function(err){
fs.unlink(video.dir,function(err){
++completedCheck
if(err){
fs.stat(video.dir,function(err){
if(!err){
s.file('delete',video.dir)
}
})
}
if(videosToDelete.length === completedCheck){
videosToDelete = videosToDelete.join(' OR ')
s.sqlQuery('DELETE FROM Videos WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
deleteVideos()
})
}
})
})
s.setDiskUsedForGroup(e,-(video.size/1000000))
s.tx({
f: 'video_delete',
ff: 'over_max',
filename: s.formattedTime(video.time)+'.'+video.ext,
mid: video.mid,
ke: video.ke,
time: video.time,
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
},'GRP_'+e.ke)
var deleteSetOfTimelapseFrames = function(err,frames,storageIndex,callback){
var framesToDelete = []
var queryValues = [e.ke]
var completedCheck = 0
if(frames){
frames.forEach(function(frame){
var selectedDate = frame.filename.split('T')[0]
var dir = s.getTimelapseFrameDirectory(frame)
var fileLocationMid = `${dir}` + frame.filename
framesToDelete.push('(mid=? AND `time`=?)')
queryValues.push(frame.mid)
queryValues.push(frame.time)
fs.unlink(fileLocationMid,function(err){
++completedCheck
if(err){
fs.stat(fileLocationMid,function(err){
if(!err){
s.file('delete',fileLocationMid)
}
})
}else{
console.log(err)
}
if(videosToDelete.length === 0){
finish()
if(framesToDelete.length === completedCheck){
framesToDelete = framesToDelete.join(' OR ')
s.sqlQuery('DELETE FROM `Timelapse Frames` WHERE ke =? AND ('+framesToDelete+')',queryValues,function(){
reRunCheck()
})
}
})
if(storageIndex){
s.setDiskUsedForGroupAddStorage(e,{
size: -(frame.size/1000000),
storageIndex: storageIndex
},'timelapeFrames')
}else{
s.setDiskUsedForGroup(e,-(frame.size/1000000),'timelapeFrames')
}
// s.tx({
// f: 'timelapse_frame_delete',
// ff: 'over_max',
// filename: s.formattedTime(video.time)+'.'+video.ext,
// mid: video.mid,
// ke: video.ke,
// time: video.time,
// end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
// },'GRP_'+e.ke)
})
}else{
finish()
console.log(err)
}
if(framesToDelete.length === 0){
if(callback)callback()
}
}
deleteVideos()
var deleteSetOfFileBinFiles = function(err,files,storageIndex,callback){
var filesToDelete = []
var queryValues = [e.ke]
var completedCheck = 0
if(files){
files.forEach(function(file){
var dir = s.getFileBinDirectory(file)
var fileLocationMid = `${dir}` + file.name
filesToDelete.push('(mid=? AND `name`=?)')
queryValues.push(file.mid)
queryValues.push(file.name)
fs.unlink(fileLocationMid,function(err){
++completedCheck
if(err){
fs.stat(fileLocationMid,function(err){
if(!err){
s.file('delete',fileLocationMid)
}
})
}
if(filesToDelete.length === completedCheck){
filesToDelete = filesToDelete.join(' OR ')
s.sqlQuery('DELETE FROM `Files` WHERE ke =? AND ('+filesToDelete+')',queryValues,function(){
reRunCheck()
})
}
})
if(storageIndex){
s.setDiskUsedForGroupAddStorage(e,{
size: -(file.size/1000000),
storageIndex: storageIndex
},'fileBin')
}else{
s.setDiskUsedForGroup(e,-(file.size/1000000),'fileBin')
}
})
}else{
console.log(err)
}
if(framesToDelete.length === 0){
if(callback)callback()
}
}
var deleteMainVideos = function(callback){
reRunCheck = function(){
return deleteMainVideos(callback)
}
//run purge command
if(s.group[e.ke].usedSpaceVideos > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? AND details NOT LIKE \'%"dir"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,rows){
deleteSetOfVideos(err,rows,null,callback)
})
}else{
callback()
}
}
var deleteAddStorageVideos = function(callback){
reRunCheck = function(){
return deleteAddStorageVideos(callback)
}
var currentStorageNumber = 0
var readStorageArray = function(finishedReading){
setTimeout(function(){
reRunCheck = readStorageArray
var storage = s.listOfStorage[currentStorageNumber]
if(!storage){
//done all checks, move on to next user
callback()
return
}
var storageId = storage.value
if(storageId === '' || !s.group[e.ke].addStorageUse[storageId]){
++currentStorageNumber
readStorageArray()
return
}
var storageIndex = s.group[e.ke].addStorageUse[storageId]
//run purge command
if(storageIndex.usedSpace > (storageIndex.sizeLimit * (storageIndex.deleteOffset || config.cron.deleteOverMaxOffset))){
s.sqlQuery('SELECT * FROM Videos WHERE status != 0 AND details NOT LIKE \'%"archived":"1"%\' AND ke=? AND details LIKE ? ORDER BY `time` ASC LIMIT 3',[e.ke,`%"dir":"${storage.value}"%`],function(err,rows){
deleteSetOfVideos(err,rows,storageIndex,callback)
})
}else{
++currentStorageNumber
readStorageArray()
}
})
}
readStorageArray()
}
var deleteTimelapseFrames = function(callback){
reRunCheck = function(){
return deleteTimelapseFrames(callback)
}
//run purge command
if(s.group[e.ke].usedSpaceTimelapseFrames > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitTimelapseFramesPercent / 100) * config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE ke=? AND details NOT LIKE \'%"archived":"1"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,frames){
deleteSetOfTimelapseFrames(err,frames,null,callback)
})
}else{
callback()
}
}
var deleteFileBinFiles = function(callback){
if(config.deleteFileBinsOverMax === true){
reRunCheck = function(){
return deleteSetOfFileBinFiles(callback)
}
//run purge command
if(s.group[e.ke].usedSpaceFileBin > (s.group[e.ke].sizeLimit * (s.group[e.ke].sizeLimitFileBinPercent / 100) * config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM `Files` WHERE ke=? ORDER BY `time` ASC LIMIT 1',[e.ke],function(err,frames){
deleteSetOfFileBinFiles(err,frames,null,callback)
})
}else{
callback()
}
}else{
callback()
}
}
deleteMainVideos(function(){
deleteTimelapseFrames(function(){
deleteFileBinFiles(function(){
deleteAddStorageVideos(function(){
finish()
})
})
})
})
}
checkQueue()
}
@ -83,27 +265,41 @@ module.exports = function(s,config){
s.sendDiskUsedAmountToClients(e)
}
}
s.setDiskUsedForGroup = function(e,bytes){
s.setDiskUsedForGroup = function(e,bytes,storagePoint){
//`bytes` will be used as the value to add or substract
if(s.group[e.ke] && s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('set',bytes)
s.group[e.ke].diskUsedEmitter.emit('set',bytes,storagePoint)
}
}
s.purgeCloudDiskForGroup = function(e,storageType){
if(s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('purgeCloud',storageType)
s.setDiskUsedForGroupAddStorage = function(e,data,storagePoint){
if(s.group[e.ke] && s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('setAddStorage',data,storagePoint)
}
}
s.setCloudDiskUsedForGroup = function(e,usage){
//`bytes` will be used as the value to add or substract
s.purgeCloudDiskForGroup = function(e,storageType,storagePoint){
if(s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('setCloud',usage)
s.group[e.ke].diskUsedEmitter.emit('purgeCloud',storageType,storagePoint)
}
}
s.setCloudDiskUsedForGroup = function(e,usage,storagePoint){
//`usage` will be used as the value to add or substract
if(s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('setCloud',usage,storagePoint)
}
}
s.sendDiskUsedAmountToClients = function(e){
//send the amount used disk space to connected users
if(s.group[e.ke]&&s.group[e.ke].init){
s.tx({f:'diskUsed',size:s.group[e.ke].usedSpace,limit:s.group[e.ke].sizeLimit},'GRP_'+e.ke);
s.tx({
f: 'diskUsed',
size: s.group[e.ke].usedSpace,
usedSpace: s.group[e.ke].usedSpace,
usedSpaceVideos: s.group[e.ke].usedSpaceVideos,
usedSpaceFilebin: s.group[e.ke].usedSpaceFilebin,
usedSpaceTimelapseFrames: s.group[e.ke].usedSpaceTimelapseFrames,
limit: s.group[e.ke].sizeLimit,
addStorage: s.group[e.ke].addStorageUse
},'GRP_'+e.ke);
}
}
//user log
@ -125,15 +321,20 @@ module.exports = function(s,config){
if(!s.group[e.ke].init){
s.group[e.ke].init={}
}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse={}};
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
if(!s.group[e.ke].users){s.group[e.ke].users={}}
if(!s.group[e.ke].dashcamUsers){s.group[e.ke].dashcamUsers={}}
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse = {}}
if(!e.limit||e.limit===''){e.limit=10000}else{e.limit=parseFloat(e.limit)}
//save global space limit for group key (mb)
s.group[e.ke].sizeLimit=e.limit;
s.group[e.ke].sizeLimit = e.limit || s.group[e.ke].sizeLimit || 10000
s.group[e.ke].sizeLimitVideoPercent = parseFloat(s.group[e.ke].init.size_video_percent) || 90
s.group[e.ke].sizeLimitTimelapseFramesPercent = parseFloat(s.group[e.ke].init.size_timelapse_percent) || 5
s.group[e.ke].sizeLimitFileBinPercent = parseFloat(s.group[e.ke].init.size_filebin_percent) || 5
//save global used space as megabyte value
s.group[e.ke].usedSpace=e.size/1000000;
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace || ((e.size || 0) / 1000000)
//emit the changes to connected users
s.sendDiskUsedAmountToClients(e)
}
@ -143,9 +344,9 @@ module.exports = function(s,config){
s.group[e.ke].init={};
}
s.sqlQuery('SELECT * FROM Users WHERE ke=? AND details NOT LIKE ?',[e.ke,'%"sub"%'],function(ar,r){
if(r&&r[0]){
r=r[0];
ar=JSON.parse(r.details);
if(r && r[0]){
r = r[0];
ar = JSON.parse(r.details);
//load extenders
s.loadGroupAppExtensions.forEach(function(extender){
extender(r,ar)
@ -153,7 +354,7 @@ module.exports = function(s,config){
//disk Used Emitter
if(!s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter = new events.EventEmitter()
s.group[e.ke].diskUsedEmitter.on('setCloud',function(currentChange){
s.group[e.ke].diskUsedEmitter.on('setCloud',function(currentChange,storagePoint){
var amount = currentChange.amount
var storageType = currentChange.storageType
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
@ -168,52 +369,96 @@ module.exports = function(s,config){
}
//change global size value
cloudDisk.usedSpace = cloudDisk.usedSpace + amount
switch(storagePoint){
case'timelapeFrames':
cloudDisk.usedSpaceTimelapseFrames += amount
break;
case'fileBin':
cloudDisk.usedSpaceFilebin += amount
break;
default:
cloudDisk.usedSpaceVideos += amount
break;
}
})
s.group[e.ke].diskUsedEmitter.on('purgeCloud',function(storageType){
s.group[e.ke].diskUsedEmitter.on('purgeCloud',function(storageType,storagePoint){
if(config.cron.deleteOverMax === true){
//set queue processor
var finish=function(){
// s.sendDiskUsedAmountToClients(e)
}
var deleteVideos = function(){
//run purge command
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
if(cloudDisk.sizeLimitCheck && cloudDisk.usedSpace > (cloudDisk.sizeLimit*config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE status != 0 AND ke=? AND details LIKE \'%"type":"'+storageType+'"%\' ORDER BY `time` ASC LIMIT 2',[e.ke],function(err,videos){
var videosToDelete = []
var queryValues = [e.ke]
if(!videos)return console.log(err)
videos.forEach(function(video){
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
videosToDelete.push('(mid=? AND `time`=?)')
queryValues.push(video.mid)
queryValues.push(video.time)
s.setCloudDiskUsedForGroup(e,{
amount : -(video.size/1000000),
storageType : storageType
})
s.deleteVideoFromCloudExtensionsRunner(e,storageType,video)
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
//set queue processor
var finish=function(){
// s.sendDiskUsedAmountToClients(e)
}
var deleteVideos = function(){
//run purge command
if(cloudDisk.sizeLimitCheck && cloudDisk.usedSpace > (cloudDisk.sizeLimit*config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE status != 0 AND ke=? AND details LIKE \'%"type":"'+storageType+'"%\' ORDER BY `time` ASC LIMIT 2',[e.ke],function(err,videos){
var videosToDelete = []
var queryValues = [e.ke]
if(!videos)return console.log(err)
videos.forEach(function(video){
video.dir = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext
videosToDelete.push('(mid=? AND `time`=?)')
queryValues.push(video.mid)
queryValues.push(video.time)
s.setCloudDiskUsedForGroup(e,{
amount : -(video.size/1000000),
storageType : storageType
})
if(videosToDelete.length > 0){
videosToDelete = videosToDelete.join(' OR ')
s.sqlQuery('DELETE FROM `Cloud Videos` WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
deleteVideos()
})
}else{
finish()
}
s.deleteVideoFromCloudExtensionsRunner(e,storageType,video)
})
}else{
finish()
}
if(videosToDelete.length > 0){
videosToDelete = videosToDelete.join(' OR ')
s.sqlQuery('DELETE FROM `Cloud Videos` WHERE ke =? AND ('+videosToDelete+')',queryValues,function(){
deleteVideos()
})
}else{
finish()
}
})
}else{
finish()
}
deleteVideos()
}
var deleteTimelapseFrames = function(callback){
reRunCheck = function(){
return deleteTimelapseFrames(callback)
}
//run purge command
if(cloudDisk.usedSpaceTimelapseFrames > (cloudDisk.sizeLimit * (s.group[e.ke].sizeLimitTimelapseFramesPercent / 100) * config.cron.deleteOverMaxOffset)){
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE ke=? AND details NOT LIKE \'%"archived":"1"%\' ORDER BY `time` ASC LIMIT 3',[e.ke],function(err,frames){
var framesToDelete = []
var queryValues = [e.ke]
if(!frames)return console.log(err)
frames.forEach(function(frame){
frame.dir = s.getVideoDirectory(frame) + s.formattedTime(frame.time) + '.' + frame.ext
framesToDelete.push('(mid=? AND `time`=?)')
queryValues.push(frame.mid)
queryValues.push(frame.time)
s.setCloudDiskUsedForGroup(e,{
amount : -(frame.size/1000000),
storageType : storageType
})
s.deleteVideoFromCloudExtensionsRunner(e,storageType,frame)
})
s.sqlQuery('DELETE FROM `Cloud Timelapse Frames` WHERE ke =? AND ('+framesToDelete+')',queryValues,function(){
deleteTimelapseFrames(callback)
})
})
}else{
callback()
}
}
deleteVideos(function(){
deleteTimelapseFrames(function(){
})
})
}else{
// s.sendDiskUsedAmountToClients(e)
}
})
//s.setDiskUsedForGroup
s.group[e.ke].diskUsedEmitter.on('set',function(currentChange){
s.group[e.ke].diskUsedEmitter.on('set',function(currentChange,storageType){
//validate current values
if(!s.group[e.ke].usedSpace){
s.group[e.ke].usedSpace=0
@ -225,12 +470,51 @@ module.exports = function(s,config){
}
//change global size value
s.group[e.ke].usedSpace += currentChange
switch(storageType){
case'timelapeFrames':
s.group[e.ke].usedSpaceTimelapseFrames += currentChange
break;
case'fileBin':
s.group[e.ke].usedSpaceFilebin += currentChange
break;
default:
s.group[e.ke].usedSpaceVideos += currentChange
break;
}
//remove value just used from queue
s.sendDiskUsedAmountToClients(e)
})
s.group[e.ke].diskUsedEmitter.on('setAddStorage',function(data,storageType){
var currentSize = data.size
var storageIndex = data.storageIndex
//validate current values
if(!storageIndex.usedSpace){
storageIndex.usedSpace = 0
}else{
storageIndex.usedSpace = parseFloat(storageIndex.usedSpace)
}
if(storageIndex.usedSpace < 0 || isNaN(storageIndex.usedSpace)){
storageIndex.usedSpace = 0
}
//change global size value
storageIndex.usedSpace += currentSize
switch(storageType){
case'timelapeFrames':
storageIndex.usedSpaceTimelapseFrames += currentSize
break;
case'fileBin':
storageIndex.usedSpaceFilebin += currentSize
break;
default:
storageIndex.usedSpaceVideos += currentSize
break;
}
//remove value just used from queue
s.sendDiskUsedAmountToClients(e)
})
}
Object.keys(ar).forEach(function(v){
s.group[e.ke].init[v]=ar[v]
s.group[e.ke].init[v] = ar[v]
})
}
})
@ -259,6 +543,7 @@ module.exports = function(s,config){
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
d.form.details.landing_page=d.d.landing_page
//check
if(d.d.edit_days == "0"){
d.form.details.days = d.d.days;
@ -278,8 +563,38 @@ module.exports = function(s,config){
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)
var newSize = parseFloat(d.form.details.size) || 10000
//load addStorageUse
var currentStorageNumber = 0
var readStorageArray = function(){
var storage = s.listOfStorage[currentStorageNumber]
if(!storage){
//done all checks, move on to next user
return
}
var path = storage.value
if(path === ''){
++currentStorageNumber
readStorageArray()
return
}
var detailContainer = d.form.details || s.group[r.ke].init
var storageId = path
var detailsContainerAddStorage = s.parseJSON(detailContainer.addStorage)
if(!s.group[d.ke].addStorageUse[storageId])s.group[d.ke].addStorageUse[storageId] = {}
var storageIndex = s.group[d.ke].addStorageUse[storageId]
storageIndex.name = storage.name
storageIndex.path = path
storageIndex.usedSpace = storageIndex.usedSpace || 0
if(detailsContainerAddStorage && detailsContainerAddStorage[path] && detailsContainerAddStorage[path].limit){
storageIndex.sizeLimit = parseFloat(detailsContainerAddStorage[path].limit)
}else{
storageIndex.sizeLimit = newSize
}
}
readStorageArray()
///
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)};

20
libs/version.js Normal file
View file

@ -0,0 +1,20 @@
var exec = require('child_process').exec
module.exports = function(s,config,lang,app,io){
var getRepositoryCommitId = function(callback){
exec(`git rev-parse HEAD`,function(err,response){
if(response){
var data = response.toString()
var isGitRespository = false
if(data.indexOf('not a git repository') === -1){
s.currentVersion = data
isGitRespository = true
s.systemLog(`Current Version : ${s.currentVersion}`)
}
}else if(err){
s.debugLog('Git is not installed.')
}
if(callback)callback(!isGitRespository,data)
})
}
getRepositoryCommitId()
}

65
libs/videoDropInServer.js Normal file
View file

@ -0,0 +1,65 @@
var fs = require('fs')
var express = require('express')
module.exports = function(s,config,lang,app,io){
if(config.videoDropInServer === true){
if(!config.videoDropInServerPort)config.videoDropInServerPort = 420
if(!config.videoDropInServerUrl)config.videoDropInServerUrl = `ftp://0.0.0.0:${config.videoDropInServerPort}`
config.videoDropInServerUrl = config.videoDropInServerUrl.replace('{{PORT}}',config.videoDropInServerPort)
const FtpSrv = require('ftp-srv')
const videoDropInServer = new FtpSrv({
url: config.videoDropInServerUrl,
// log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){}
})
videoDropInServer.on('login', (data, resolve, reject) => {
var username = data.username
var password = data.password
s.basicOrApiAuthentication(username,password,function(err,user){
if(user){
data.connection.on('STOR', (error, filePath) => {
if(!error && filePath){
var filenameParts = filePath.replace(s.dir.videos + user.ke + '/','').split('/')
var ke = user.ke
var mid = filenameParts[0].replace('_timelapse','')
var monitor = s.group[ke].rawMonitorConfigurations[mid]
var filename = filenameParts[filenameParts.length - 1]
if(s.isCorrectFilenameSyntax(filename)){
if(filenameParts[0].indexOf('_timelapse')){
var fileStats = fs.statSync(filePath)
var details = {}
if(monitor.details && monitor.details.dir && monitor.details.dir !== ''){
details.dir = monitor.details.dir
}
var timeNow = new Date(s.nameToTime(filename))
s.sqlQuery('INSERT INTO `Timelapse Frames` (ke,mid,details,filename,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),filename,fileStats.size,timeNow])
s.setDiskUsedForGroup(monitor,fileStats.size / 1000000)
}
// else{
// s.insertDatabaseRow(
// monitor,
// {
//
// }
// )
// console.log(filename)
// }
}else{
console.log('Incorrect Filename Syntax')
}
}else{
s.systemLog(error)
}
})
resolve({root: s.dir.videos + user.ke})
}else{
// reject(new Error('Failed Authorization'))
}
})
})
videoDropInServer.listen().then(() => {
s.systemLog(`Video Drop-In Server (FTP) running on port ${config.videoDropInServerPort}...`)
}).catch(function(err){
s.systemLog(err)
})
}
}

View file

@ -26,7 +26,7 @@ module.exports = function(s,config,lang){
*/
s.buildVideoLinks = function(videos,options){
videos.forEach(function(v){
var details = JSON.parse(v.details)
var details = s.parseJSON(v.details)
var queryString = []
if(details.isUTC === true){
queryString.push('isUTC=true')
@ -45,7 +45,7 @@ module.exports = function(s,config,lang){
}
v.filename = s.formattedTime(v.time)+'.'+v.ext;
if(!options.videoParam)options.videoParam = 'videos'
var href = '/'+options.auth+'/'+options.videoParam+'/'+v.ke+'/'+v.mid+'/'+v.filename;
var href = s.checkCorrectPathEnding(config.webPaths.apiPrefix) + options.auth+'/'+options.videoParam+'/'+v.ke+'/'+v.mid+'/'+v.filename;
v.actionUrl = href
v.links = {
deleteVideo : href+'/delete' + queryString,
@ -56,16 +56,11 @@ module.exports = function(s,config,lang){
v.details = details
})
}
//extender for "s.insertCompletedVideo"
s.insertCompletedVideoExtensions = []
s.insertCompletedVideoExtender = function(callback){
s.insertCompletedVideoExtensions.push(callback)
}
s.insertDatabaseRow = function(e,k,callback){
s.checkDetails(e)
//save database row
k.details = {}
if(e.details&&e.details.dir&&e.details.dir!==''){
if(!k.details)k.details = {}
if(e.details && e.details.dir && e.details.dir !== ''){
k.details.dir = e.details.dir
}
if(config.useUTC === true)k.details.isUTC = config.useUTC;
@ -94,8 +89,8 @@ module.exports = function(s,config,lang){
if(!k)k={};
e.dir = s.getVideoDirectory(e)
k.dir = e.dir.toString()
if(s.group[e.ke].mon[e.id].childNode){
s.cx({f:'insertCompleted',d:s.group[e.ke].mon_conf[e.id],k:k},s.group[e.ke].mon[e.id].childNodeId);
if(s.group[e.ke].activeMonitors[e.id].childNode){
s.cx({f:'insertCompleted',d:s.group[e.ke].rawMonitorConfigurations[e.id],k:k},s.group[e.ke].activeMonitors[e.id].childNodeId);
}else{
//get file directory
k.fileExists = fs.existsSync(k.dir+k.file)
@ -113,6 +108,7 @@ module.exports = function(s,config,lang){
}
if(k.fileExists===true){
//close video row
k.details = {}
k.stat = fs.statSync(k.dir+k.file)
k.filesize = k.stat.size
k.filesizeMB = parseFloat((k.filesize/1000000).toFixed(2))
@ -145,8 +141,8 @@ module.exports = function(s,config,lang){
})
})
.on('close',function(){
clearTimeout(s.group[e.ke].mon[e.id].recordingChecker)
clearTimeout(s.group[e.ke].mon[e.id].streamChecker)
clearTimeout(s.group[e.ke].activeMonitors[e.id].recordingChecker)
clearTimeout(s.group[e.ke].activeMonitors[e.id].streamChecker)
s.cx({
f:'created_file',
mid:e.id,
@ -174,7 +170,18 @@ module.exports = function(s,config,lang){
//purge over max
s.purgeDiskForGroup(e)
//send new diskUsage values
s.setDiskUsedForGroup(e,k.filesizeMB)
var storageIndex = s.getVideoStorageIndex(e)
if(storageIndex){
s.setDiskUsedForGroupAddStorage(e,{
size: k.filesizeMB,
storageIndex: storageIndex
})
}else{
s.setDiskUsedForGroup(e,k.filesizeMB)
}
s.onBeforeInsertCompletedVideoExtensions.forEach(function(extender){
extender(e,k)
})
s.insertDatabaseRow(e,k,callback)
s.insertCompletedVideoExtensions.forEach(function(extender){
extender(e,k)
@ -216,7 +223,15 @@ module.exports = function(s,config,lang){
time: s.nameToTime(filename),
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
},'GRP_'+e.ke);
s.setDiskUsedForGroup(e,-(r.size / 1000000))
var storageIndex = s.getVideoStorageIndex(e)
if(storageIndex){
s.setDiskUsedForGroupAddStorage(e,{
size: -(r.size / 1000000),
storageIndex: storageIndex
})
}else{
s.setDiskUsedForGroup(e,-(r.size / 1000000))
}
s.sqlQuery('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',queryValues,function(err){
if(err){
s.systemLog(lang['File Delete Error'] + ' : '+e.ke+' : '+' : '+e.id,err)
@ -270,7 +285,15 @@ module.exports = function(s,config,lang){
time: s.nameToTime(filename),
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
},'GRP_'+video.ke);
s.setDiskUsedForGroup(video,-(video.size / 1000000))
var storageIndex = s.getVideoStorageIndex(video)
if(storageIndex){
s.setDiskUsedForGroupAddStorage(video,{
size: -(video.size / 1000000),
storageIndex: storageIndex
})
}else{
s.setDiskUsedForGroup(video,-(video.size / 1000000))
}
fs.unlink(video.dir+filename,function(err){
fs.stat(video.dir+filename,function(err){
if(!err){
@ -334,8 +357,9 @@ module.exports = function(s,config,lang){
}
if(forceCheck === true || config.insertOrphans === true){
if(!checkMax){
checkMax = config.orphanedVideoCheckMax
checkMax = config.orphanedVideoCheckMax || 2
}
var videosDirectory = s.getVideoDirectory(monitor)// + s.formattedTime(video.time) + '.' + video.ext
fs.readdir(videosDirectory,function(err,files){
if(files && files.length > 0){
@ -380,7 +404,7 @@ module.exports = function(s,config,lang){
}
s.streamMp4FileOverHttp = function(filePath,req,res){
var ext = filePath.split('.')
ext = filePath[filePath.length - 1]
ext = ext[ext.length - 1]
var total = fs.statSync(filePath).size;
if (req.headers['range']) {
try{
@ -392,15 +416,15 @@ module.exports = function(s,config,lang){
var end = partialend ? parseInt(partialend, 10) : total-1;
var chunksize = (end-start)+1;
var file = fs.createReadStream(filePath, {start: start, end: end});
req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext }
req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+ext }
req.writeCode=206
}catch(err){
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
var file = fs.createReadStream(filePath)
req.writeCode=200
}
} else {
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
var file = fs.createReadStream(filePath)
req.writeCode=200
}
@ -414,4 +438,116 @@ module.exports = function(s,config,lang){
file.pipe(res)
return file
}
s.createVideoFromTimelapse = function(timelapseFrames,framesPerSecond,callback){
framesPerSecond = parseInt(framesPerSecond)
if(!framesPerSecond || isNaN(framesPerSecond))framesPerSecond = 2
var frames = timelapseFrames.reverse()
var ke = frames[0].ke
var mid = frames[0].mid
var finalFileName = frames[0].filename.split('.')[0] + '_' + frames[frames.length - 1].filename.split('.')[0] + `-${framesPerSecond}fps`
var concatFiles = []
var createLocation
frames.forEach(function(frame,frameNumber){
var selectedDate = frame.filename.split('T')[0]
var fileLocationMid = `${frame.ke}/${frame.mid}_timelapse/${selectedDate}/`
frame.details = s.parseJSON(frame.details)
var fileLocation
if(frame.details.dir){
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
}else{
fileLocation = `${s.dir.videos}`
}
fileLocation = `${fileLocation}${fileLocationMid}${frame.filename}`
concatFiles.push(fileLocation)
if(frameNumber === 0){
createLocation = fileLocationMid
}
})
if(concatFiles.length > 30){
var commandTempLocation = `${s.dir.streams}${ke}/${mid}/mergeJpegs_${finalFileName}.sh`
var finalMp4OutputLocation = `${s.dir.fileBin}${ke}/${mid}/${finalFileName}.mp4`
if(!s.group[ke].activeMonitors[mid].buildingTimelapseVideo){
if(!fs.existsSync(finalMp4OutputLocation)){
var currentFile = 0
var completionTimeout
var commandString = `ffmpeg -y -pattern_type glob -f image2pipe -vcodec mjpeg -r ${framesPerSecond} -analyzeduration 10 -i - -q:v 1 -c:v libx264 -r ${framesPerSecond} "${finalMp4OutputLocation}"`
fs.writeFileSync(commandTempLocation,commandString)
var videoBuildProcess = spawn('sh',[commandTempLocation])
videoBuildProcess.stderr.on('data',function(data){
// console.log(data.toString())
clearTimeout(completionTimeout)
completionTimeout = setTimeout(function(){
if(currentFile === concatFiles.length - 1){
videoBuildProcess.kill('SIGTERM')
}
},4000)
})
videoBuildProcess.on('exit',function(data){
var timeNow = new Date()
var fileStats = fs.statSync(finalMp4OutputLocation)
var details = {}
s.sqlQuery('INSERT INTO `Files` (ke,mid,details,name,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),finalFileName + '.mp4',fileStats.size,timeNow])
s.setDiskUsedForGroup({ke: ke},fileStats.size / 1000000,'fileBin')
fs.unlink(commandTempLocation,function(){
})
delete(s.group[ke].activeMonitors[mid].buildingTimelapseVideo)
})
var readFile = function(){
var filePath = concatFiles[currentFile]
// console.log(filePath,currentFile,'/',concatFiles.length - 1)
fs.readFile(filePath,function(err,buffer){
if(!err)videoBuildProcess.stdin.write(buffer)
if(currentFile === concatFiles.length - 1){
//is last
}else{
setTimeout(function(){
++currentFile
readFile()
},1/framesPerSecond)
}
})
}
readFile()
s.group[ke].activeMonitors[mid].buildingTimelapseVideo = videoBuildProcess
callback({
ok: true,
fileExists: false,
fileLocation: finalMp4OutputLocation,
msg: lang['Started Building']
})
}else{
callback({
ok: false,
fileExists: true,
fileLocation: finalMp4OutputLocation,
msg: lang['Already exists']
})
}
}else{
callback({
ok: false,
fileExists: false,
fileLocation: finalMp4OutputLocation,
msg: lang.Building
})
}
}else{
callback({
ok: false,
fileExists: false,
msg: lang.notEnoughFramesText1
})
}
}
s.getVideoStorageIndex = function(video){
var details = s.parseJSON(video.details) || {}
var storageId = details.storageId
if(s.group[video.ke] && s.group[video.ke].activeMonitors[video.id] && s.group[video.ke].activeMonitors[video.id].addStorageId)storageId = s.group[video.ke].activeMonitors[video.id].addStorageId
if(storageId){
return s.group[video.ke].addStorageUse[storageId]
}
return null
}
}

View file

@ -44,6 +44,8 @@ module.exports = function(s,config,lang,io){
if(config.renderPaths.dashcam === undefined){config.renderPaths.dashcam='pages/dashcam'}
//embeddable widget page
if(config.renderPaths.embed === undefined){config.renderPaths.embed='pages/embed'}
//timelapse page (not modal)
if(config.renderPaths.timelapse === undefined){config.renderPaths.timelapse='pages/timelapse'}
//mjpeg full screen page
if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
//gridstack only page
@ -53,6 +55,8 @@ module.exports = function(s,config,lang,io){
// Use uws/cws
if(config.useUWebsocketJs === undefined){config.useUWebsocketJs=true}
//SSL options
var wellKnownDirectory = s.mainDirectory + '/web/.well-known'
if(fs.existsSync(wellKnownDirectory))app.use('/.well-known',express.static(wellKnownDirectory))
if(config.ssl&&config.ssl.key&&config.ssl.cert){
config.ssl.key=fs.readFileSync(s.checkRelativePath(config.ssl.key),'utf8')
config.ssl.cert=fs.readFileSync(s.checkRelativePath(config.ssl.cert),'utf8')

View file

@ -68,7 +68,7 @@ module.exports = function(s,config,lang,app){
s.closeJsonResponse(res,endData)
return
}
var form = s.getPostData(req)
var form = s.getPostData(req) || {}
var uid = form.uid || s.getPostData(req,'uid',false)
var mail = form.mail || s.getPostData(req,'mail',false)
s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[uid,req.params.ke,mail])
@ -196,7 +196,7 @@ module.exports = function(s,config,lang,app){
}
}else{
if(!user.details.sub || user.details.allmonitors === '1' || user.details.monitor_edit.indexOf(req.params.id) > -1 || hasRestrictions && user.details.monitor_create === '1'){
s.userLog(s.group[req.params.ke].mon_conf[req.params.id],{type:'Monitor Deleted',msg:'by user : '+user.uid});
s.userLog(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{type:'Monitor Deleted',msg:'by user : '+user.uid});
req.params.delete=1;s.camera('stop',req.params);
s.tx({f:'monitor_delete',uid:user.uid,mid:req.params.id,ke:req.params.ke},'GRP_'+req.params.ke);
s.sqlQuery('DELETE FROM Monitors WHERE ke=? AND mid=?',[req.params.ke,req.params.id])

View file

@ -11,15 +11,16 @@ var httpProxy = require('http-proxy');
var onvif = require('node-onvif');
var proxy = httpProxy.createProxyServer({})
var ejs = require('ejs');
var CircularJSON = require('circular-json');
module.exports = function(s,config,lang,app,io){
if(config.productType==='Pro'){
var LdapAuth = require('ldapauth-fork');
}
s.renderPage = function(req,res,paths,passables,callback){
passables.window = {}
passables.data = req.params
passables.originalURL = s.getOriginalUrl(req)
passables.config = config
passables.baseUrl = req.protocol+'://'+req.hostname
passables.config = s.getConfigWithBranding(req.hostname)
res.render(paths,passables,callback)
}
//child node proxy check
@ -28,8 +29,8 @@ module.exports = function(s,config,lang,app,io){
//res = response, only needed for express (http server)
//request = request, only needed for express (http server)
s.checkChildProxy = function(params,cb,res,req){
if(s.group[params.ke] && s.group[params.ke].mon[params.id] && s.group[params.ke].mon[params.id].childNode){
var url = 'http://' + s.group[params.ke].mon[params.id].childNode// + req.originalUrl
if(s.group[params.ke] && s.group[params.ke].activeMonitors[params.id] && s.group[params.ke].activeMonitors[params.id].childNode){
var url = 'http://' + s.group[params.ke].activeMonitors[params.id].childNode// + req.originalUrl
proxy.web(req, res, { target: url })
}else{
cb()
@ -94,34 +95,22 @@ module.exports = function(s,config,lang,app,io){
* Page : Login Screen
*/
app.get(config.webPaths.home, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'dashboard'},function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
})
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'dashboard'})
});
/**
* Page : Administrator Login Screen
*/
app.get(config.webPaths.admin, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'admin'},function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
})
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'admin'})
});
/**
* Page : Superuser Login Screen
*/
app.get(config.webPaths.super, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'super'},function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
s.renderPage(req,res,config.renderPaths.index,{
lang: lang,
config: s.getConfigWithBranding(req.hostname),
screen: 'super'
})
});
/**
@ -183,13 +172,8 @@ module.exports = function(s,config,lang,app,io){
failedLogin: true,
message: lang.failedLoginText1,
lang: s.copySystemDefaultLanguage(),
config: config,
config: s.getConfigWithBranding(req.hostname),
screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
})
}
return false
@ -207,12 +191,7 @@ module.exports = function(s,config,lang,app,io){
res.end(s.prettyPrint(data))
}else{
data.screen=req.params.screen
s.renderPage(req,res,focus,data,function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
})
s.renderPage(req,res,focus,data)
}
}
failedAuthentication = function(board){
@ -241,13 +220,8 @@ module.exports = function(s,config,lang,app,io){
failedLogin: true,
message: lang.failedLoginText2,
lang: s.copySystemDefaultLanguage(),
config: config,
config: s.getConfigWithBranding(req.hostname),
screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
s.systemLog(err)
}
res.end(html)
})
}
var logTo = {
@ -284,7 +258,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"dashcam"],function(err,rr){
req.resp.mons=rr;
renderPage(config.renderPaths.dashcam,{
// config: config,
// config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
lang: r.lang,
define: s.getDefinitonFile(r.details.lang),
@ -296,7 +270,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"socket"],function(err,rr){
req.resp.mons=rr;
renderPage(config.renderPaths.streamer,{
// config: config,
// config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
lang: r.lang,
define: s.getDefinitonFile(r.details.lang),
@ -309,7 +283,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT uid,mail,details FROM Users WHERE ke=? AND details LIKE \'%"sub"%\'',[r.ke],function(err,rr) {
s.sqlQuery('SELECT * FROM Monitors WHERE ke=?',[r.ke],function(err,rrr) {
renderPage(config.renderPaths.admin,{
config: config,
config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
$subs: rr,
$mons: rrr,
@ -321,9 +295,13 @@ module.exports = function(s,config,lang,app,io){
})
}else{
//not admin user
renderPage(config.renderPaths.home,{
var chosenRender = 'home'
if(r.details.landing_page && r.details.landing_page !== '' && config.renderPaths[r.details.landing_page]){
chosenRender = r.details.landing_page
}
renderPage(config.renderPaths[chosenRender],{
$user:req.resp,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:r.lang,
define:s.getDefinitonFile(r.details.lang),
addStorage:s.dir.addStorage,
@ -334,16 +312,20 @@ module.exports = function(s,config,lang,app,io){
}
break;
default:
renderPage(config.renderPaths.home,{
var chosenRender = 'home'
if(r.details.sub && r.details.landing_page && r.details.landing_page !== '' && config.renderPaths[r.details.landing_page]){
chosenRender = r.details.landing_page
}
renderPage(config.renderPaths[chosenRender],{
$user:req.resp,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:r.lang,
define:s.getDefinitonFile(r.details.lang),
addStorage:s.dir.addStorage,
fs:fs,
__dirname:s.mainDirectory,
customAutoLoad: s.customAutoLoadTree
});
})
break;
}
s.userLog({ke:r.ke,mid:'$USER'},{type:r.lang['New Authentication Token'],msg:{for:req.body.function,mail:r.mail,id:r.uid,ip:req.ip}})
@ -360,6 +342,8 @@ module.exports = function(s,config,lang,app,io){
r.details=JSON.parse(r.details);
r.lang=s.getLanguageFile(r.details.lang)
req.factorAuth=function(cb){
req.params.auth = r.auth
req.params.ke = r.ke
if(r.details.factorAuth === "1"){
if(!r.details.acceptedMachines||!(r.details.acceptedMachines instanceof Object)){
r.details.acceptedMachines={}
@ -494,8 +478,8 @@ module.exports = function(s,config,lang,app,io){
req.resp.lang=r.lang
s.sqlQuery('INSERT INTO Users (ke,uid,auth,mail,pass,details) VALUES (?,?,?,?,?,?)',user.post)
}
req.resp.details=JSON.stringify(req.resp.details)
req.resp.auth_token=req.resp.auth
req.resp.details = JSON.stringify(req.resp.details)
req.resp.auth_token = req.resp.auth
req.resp.ok=true
checkRoute(req.resp)
})
@ -534,6 +518,7 @@ module.exports = function(s,config,lang,app,io){
}
data.Logs = r
data.customAutoLoad = s.customAutoLoadTree
data.currentVersion = s.currentVersion
fs.readFile(s.location.config,'utf8',function(err,file){
data.plainConfig = JSON.parse(file)
renderPage(config.renderPaths.super,data)
@ -644,8 +629,8 @@ module.exports = function(s,config,lang,app,io){
r = filteredByGroup;
}
r.forEach(function(v,n){
if(s.group[v.ke]&&s.group[v.ke].mon[v.mid]&&s.group[v.ke].mon[v.mid].watch){
r[n].currentlyWatching=Object.keys(s.group[v.ke].mon[v.mid].watch).length
if(s.group[v.ke]&&s.group[v.ke].activeMonitors[v.mid]&&s.group[v.ke].activeMonitors[v.mid].watch){
r[n].currentlyWatching=Object.keys(s.group[v.ke].activeMonitors[v.mid].watch).length
}
r[n].subStream={}
var details = JSON.parse(r[n].details)
@ -689,7 +674,7 @@ module.exports = function(s,config,lang,app,io){
s.renderPage(req,res,page,{
data:Object.assign(req.params,req.query),
baseUrl:req.protocol+'://'+req.hostname,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:user.lang,
$user:user,
monitors:r,
@ -866,10 +851,10 @@ module.exports = function(s,config,lang,app,io){
}
s.sqlQuery(req.sql,req.ar,function(err,r){
r.forEach(function(v,n){
if(s.group[v.ke] && s.group[v.ke].mon[v.mid]){
r[n].currentlyWatching = Object.keys(s.group[v.ke].mon[v.mid].watch).length
r[n].currentCpuUsage = s.group[v.ke].mon[v.mid].currentCpuUsage
r[n].status = s.group[v.ke].mon[v.mid].monitorStatus
if(s.group[v.ke] && s.group[v.ke].activeMonitors[v.mid]){
r[n].currentlyWatching = Object.keys(s.group[v.ke].activeMonitors[v.mid].watch).length
r[n].currentCpuUsage = s.group[v.ke].activeMonitors[v.mid].currentCpuUsage
r[n].status = s.group[v.ke].activeMonitors[v.mid].monitorStatus
}
var buildStreamURL = function(type,channelNumber){
var streamURL
@ -909,7 +894,7 @@ module.exports = function(s,config,lang,app,io){
r[n].streamsSortedByType={}
buildStreamURL(details.stream_type)
if(details.stream_channels&&details.stream_channels!==''){
details.stream_channels=JSON.parse(details.stream_channels)
details.stream_channels=s.parseJSON(details.stream_channels)
details.stream_channels.forEach(function(b,m){
buildStreamURL(b.stream_type,m.toString())
})
@ -1096,7 +1081,10 @@ module.exports = function(s,config,lang,app,io){
/**
* API : Get Events
*/
app.get([config.webPaths.apiPrefix+':auth/events/:ke',config.webPaths.apiPrefix+':auth/events/:ke/:id',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start/:end'], function (req,res){
app.get([
config.webPaths.apiPrefix+':auth/events/:ke',
config.webPaths.apiPrefix+':auth/events/:ke/:id'
], function (req,res){
req.ret={ok:false};
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
@ -1122,20 +1110,42 @@ module.exports = function(s,config,lang,app,io){
return;
}
}
if(req.params.start&&req.params.start!==''){
req.params.start = s.stringToSqlTime(req.params.start)
if(req.params.end&&req.params.end!==''){
req.params.end = s.stringToSqlTime(req.params.end)
req.sql+=' AND `time` >= ? AND `time` <= ?';
req.ar.push(decodeURIComponent(req.params.start))
req.ar.push(decodeURIComponent(req.params.end))
}else{
req.sql+=' AND `time` >= ?';
req.ar.push(decodeURIComponent(req.params.start))
if(req.query.start||req.query.end){
if(req.query.start && req.query.start !== ''){
req.query.start = s.stringToSqlTime(req.query.start)
}
if(req.query.end && req.query.end !== ''){
req.query.end = s.stringToSqlTime(req.query.end)
}
if(!req.query.startOperator||req.query.startOperator==''){
req.query.startOperator='>='
}
if(!req.query.endOperator||req.query.endOperator==''){
req.query.endOperator='<='
}
switch(true){
case(req.query.start&&req.query.start!==''&&req.query.end&&req.query.end!==''):
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `time` '+req.query.endOperator+' ?';
req.ar.push(req.query.start)
req.ar.push(req.query.end)
break;
case(req.query.start&&req.query.start!==''):
req.sql+=' AND `time` '+req.query.startOperator+' ?';
req.ar.push(req.query.start)
break;
case(req.query.end&&req.query.end!==''):
req.sql+=' AND `time` '+req.query.endOperator+' ?';
req.ar.push(req.query.end)
break;
}
}
if(!req.params.limit||req.params.limit==''){req.params.limit=100}
req.sql+=' ORDER BY `time` DESC LIMIT '+req.params.limit+'';
req.sql+=' ORDER BY `time` DESC';
if(!req.query.limit||req.query.limit==''){
req.query.limit='100'
}
if(req.query.limit!=='0'){
req.sql+=' LIMIT '+req.query.limit
}
s.sqlQuery(req.sql,req.ar,function(err,r){
if(err){
err.sql=req.sql;
@ -1238,7 +1248,7 @@ module.exports = function(s,config,lang,app,io){
if(r&&r[0]){
req.ar=[];
r.forEach(function(v){
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[v.mid]&&s.group[req.params.ke].mon[v.mid].isStarted === true){
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[v.mid]&&s.group[req.params.ke].activeMonitors[v.mid].isStarted === true){
req.ar.push(v)
}
})
@ -1270,25 +1280,25 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[req.params.ke,req.params.id],function(err,r){
if(r&&r[0]){
r=r[0];
if(req.query.reset==='1'||(s.group[r.ke]&&s.group[r.ke].mon_conf[r.mid].mode!==req.params.f)||req.query.fps&&(!s.group[r.ke].mon[r.mid].currentState||!s.group[r.ke].mon[r.mid].currentState.trigger_on)){
if(req.query.reset!=='1'||!s.group[r.ke].mon[r.mid].trigger_timer){
if(!s.group[r.ke].mon[r.mid].currentState)s.group[r.ke].mon[r.mid].currentState={}
s.group[r.ke].mon[r.mid].currentState.mode=r.mode.toString()
s.group[r.ke].mon[r.mid].currentState.fps=r.fps.toString()
if(!s.group[r.ke].mon[r.mid].currentState.trigger_on){
s.group[r.ke].mon[r.mid].currentState.trigger_on=true
if(req.query.reset==='1'||(s.group[r.ke]&&s.group[r.ke].rawMonitorConfigurations[r.mid].mode!==req.params.f)||req.query.fps&&(!s.group[r.ke].activeMonitors[r.mid].currentState||!s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on)){
if(req.query.reset!=='1'||!s.group[r.ke].activeMonitors[r.mid].trigger_timer){
if(!s.group[r.ke].activeMonitors[r.mid].currentState)s.group[r.ke].activeMonitors[r.mid].currentState={}
s.group[r.ke].activeMonitors[r.mid].currentState.mode=r.mode.toString()
s.group[r.ke].activeMonitors[r.mid].currentState.fps=r.fps.toString()
if(!s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on){
s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on=true
}else{
s.group[r.ke].mon[r.mid].currentState.trigger_on=false
s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on=false
}
r.mode=req.params.f;
try{r.details=JSON.parse(r.details);}catch(er){}
if(req.query.fps){
r.fps=parseFloat(r.details.detector_trigger_record_fps)
s.group[r.ke].mon[r.mid].currentState.detector_trigger_record_fps=r.fps
s.group[r.ke].activeMonitors[r.mid].currentState.detector_trigger_record_fps=r.fps
}
r.id=r.mid;
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[r.mode,r.ke,r.mid]);
s.group[r.ke].mon_conf[r.mid]=r;
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
s.camera('stop',s.cleanMonitorObject(r));
@ -1303,7 +1313,7 @@ module.exports = function(s,config,lang,app,io){
req.ret.ok=true;
if(req.params.ff&&req.params.f!=='stop'){
req.params.ff=parseFloat(req.params.ff);
clearTimeout(s.group[r.ke].mon[r.mid].trigger_timer)
clearTimeout(s.group[r.ke].activeMonitors[r.mid].trigger_timer)
switch(req.params.fff){
case'day':case'days':
req.timeout=req.params.ff*1000*60*60*24
@ -1318,17 +1328,17 @@ module.exports = function(s,config,lang,app,io){
req.timeout=req.params.ff*1000
break;
}
s.group[r.ke].mon[r.mid].trigger_timer=setTimeout(function(){
delete(s.group[r.ke].mon[r.mid].trigger_timer)
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[s.group[r.ke].mon[r.mid].currentState.mode,r.ke,r.mid]);
s.group[r.ke].activeMonitors[r.mid].trigger_timer=setTimeout(function(){
delete(s.group[r.ke].activeMonitors[r.mid].trigger_timer)
s.sqlQuery('UPDATE Monitors SET mode=? WHERE ke=? AND mid=?',[s.group[r.ke].activeMonitors[r.mid].currentState.mode,r.ke,r.mid]);
r.neglectTriggerTimer=1;
r.mode=s.group[r.ke].mon[r.mid].currentState.mode;
r.fps=s.group[r.ke].mon[r.mid].currentState.fps;
r.mode=s.group[r.ke].activeMonitors[r.mid].currentState.mode;
r.fps=s.group[r.ke].activeMonitors[r.mid].currentState.fps;
s.camera('stop',s.cleanMonitorObject(r),function(){
if(s.group[r.ke].mon[r.mid].currentState.mode!=='stop'){
s.camera(s.group[r.ke].mon[r.mid].currentState.mode,s.cleanMonitorObject(r));
if(s.group[r.ke].activeMonitors[r.mid].currentState.mode!=='stop'){
s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
}
s.group[r.ke].mon_conf[r.mid]=r;
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
});
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
@ -1382,31 +1392,32 @@ module.exports = function(s,config,lang,app,io){
* API : Get fileBin file
*/
app.get(config.webPaths.apiPrefix+':auth/fileBin/:ke/:id/:year/:month/:day/:file', function (req,res){
req.fn=function(user){
req.failed=function(){
s.auth(req.params,function(user){
var failed = function(){
res.end(user.lang['File Not Found'])
}
if (!s.group[req.params.ke].fileBin[req.params.id+'/'+req.params.file]){
s.sqlQuery('SELECT * FROM Files WHERE ke=? AND mid=? AND name=?',[req.params.ke,req.params.id,req.params.file],function(err,r){
if(r&&r[0]){
r=r[0]
r.details=JSON.parse(r.details)
req.dir=s.dir.fileBin+req.params.ke+'/'+req.params.id+'/'+r.details.year+'/'+r.details.month+'/'+r.details.day+'/'+req.params.file;
if(fs.existsSync(req.dir)){
res.on('finish',function(){res.end();});
fs.createReadStream(req.dir).pipe(res);
}else{
req.failed()
}
r = r[0]
r.details = JSON.parse(r.details)
req.dir = s.dir.fileBin+req.params.ke+'/'+req.params.id+'/'+r.details.year+'/'+r.details.month+'/'+r.details.day+'/'+req.params.file;
fs.stat(req.dir,function(err,stats){
if(!err){
res.on('finish',function(){res.end()})
fs.createReadStream(req.dir).pipe(res)
}else{
failed()
}
})
}else{
req.failed()
failed()
}
})
}else{
res.end(user.lang['Please Wait for Completion'])
}
}
s.auth(req.params,req.fn,res,req);
},res,req);
});
/**
* API : Zip Videos and Get Link from fileBin
@ -1451,52 +1462,52 @@ module.exports = function(s,config,lang,app,io){
}
fs.unlink(zippedFile);
})
if(!fs.existsSync(fileBinDir)){
fs.mkdirSync(fileBinDir);
}
r.forEach(function(video){
var timeFormatted = s.formattedTime(video.time)
video.filename = timeFormatted+'.'+video.ext
var dir = s.getVideoDirectory(video)+video.filename
var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext
fs.writeFileSync(fileBinDir+tempVideoFile, fs.readFileSync(dir))
tempFiles.push(fileBinDir+tempVideoFile)
script += ' "'+tempVideoFile+'"'
})
fs.writeFileSync(tempScript,script,'utf8')
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
zipCreate.stderr.on('data',function(data){
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
})
zipCreate.on('exit',function(data){
fs.unlinkSync(tempScript)
tempFiles.forEach(function(file){
fs.unlink(file,function(){})
fs.mkdir(fileBinDir,function(err){
s.handleFolderError(err)
r.forEach(function(video){
var timeFormatted = s.formattedTime(video.time)
video.filename = timeFormatted+'.'+video.ext
var dir = s.getVideoDirectory(video)+video.filename
var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext
fs.writeFileSync(fileBinDir+tempVideoFile, fs.readFileSync(dir))
tempFiles.push(fileBinDir+tempVideoFile)
script += ' "'+tempVideoFile+'"'
})
res.setHeader('Content-Disposition', 'attachment; filename="'+zippedFilename+'"')
var zipDownload = fs.createReadStream(zippedFile)
zipDownload.pipe(res)
zipDownload.on('error', function (error) {
var errorString = error.toString()
s.userLog({
ke: req.params.ke,
mid: '$USER'
},{
title: 'Zip Download Error',
msg: errorString
fs.writeFileSync(tempScript,script,'utf8')
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
zipCreate.stderr.on('data',function(data){
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
})
zipCreate.on('exit',function(data){
fs.unlinkSync(tempScript)
tempFiles.forEach(function(file){
fs.unlink(file,function(){})
})
if(zipDownload && zipDownload.destroy){
res.setHeader('Content-Disposition', 'attachment; filename="'+zippedFilename+'"')
var zipDownload = fs.createReadStream(zippedFile)
zipDownload.pipe(res)
zipDownload.on('error', function (error) {
var errorString = error.toString()
s.userLog({
ke: req.params.ke,
mid: '$USER'
},{
title: 'Zip Download Error',
msg: errorString
})
if(zipDownload && zipDownload.destroy){
zipDownload.destroy()
}
res.end(s.prettyPrint({
ok: false,
msg: errorString
}))
})
zipDownload.on('close', function () {
res.end()
zipDownload.destroy()
}
res.end(s.prettyPrint({
ok: false,
msg: errorString
}))
})
zipDownload.on('close', function () {
res.end()
zipDownload.destroy()
fs.unlinkSync(zippedFile)
fs.unlinkSync(zippedFile)
})
})
})
}else{
@ -1551,64 +1562,63 @@ module.exports = function(s,config,lang,app,io){
}
fs.unlink(zippedFile);
})
if(!fs.existsSync(fileBinDir)){
fs.mkdirSync(fileBinDir);
}
var cloudDownloadCount = 0
var getFile = function(video,completed){
if(!video)completed();
s.checkDetails(video)
var filename = video.href.split('/')
filename = filename[filename.length - 1]
var timeFormatted = s.formattedTime(video.time)
var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename
var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile)
tempFileWriteStream.on('finish', function() {
++cloudDownloadCount
getFile(r[cloudDownloadCount],completed)
})
var cloudVideoDownload = request(video.href)
cloudVideoDownload.on('response', function (res) {
res.pipe(tempFileWriteStream)
})
tempFiles.push(fileBinDir+tempVideoFile)
script += ' "'+tempVideoFile+'"'
}
getFile(r[cloudDownloadCount],function(){
fs.writeFileSync(tempScript,script,'utf8')
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
zipCreate.stderr.on('data',function(data){
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
})
zipCreate.on('exit',function(data){
fs.unlinkSync(tempScript)
tempFiles.forEach(function(file){
fs.unlink(file,function(){})
fs.mkdir(fileBinDir,function(err){
var cloudDownloadCount = 0
var getFile = function(video,completed){
if(!video)completed();
s.checkDetails(video)
var filename = video.href.split('/')
filename = filename[filename.length - 1]
var timeFormatted = s.formattedTime(video.time)
var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename
var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile)
tempFileWriteStream.on('finish', function() {
++cloudDownloadCount
getFile(r[cloudDownloadCount],completed)
})
res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"')
var zipDownload = fs.createReadStream(zippedFile)
zipDownload.pipe(res)
zipDownload.on('error', function (error) {
var errorString = error.toString()
s.userLog({
ke: req.params.ke,
mid: '$USER'
},{
title: 'Zip Download Error',
msg: errorString
var cloudVideoDownload = request(video.href)
cloudVideoDownload.on('response', function (res) {
res.pipe(tempFileWriteStream)
})
tempFiles.push(fileBinDir+tempVideoFile)
script += ' "'+tempVideoFile+'"'
}
getFile(r[cloudDownloadCount],function(){
fs.writeFileSync(tempScript,script,'utf8')
var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true})
zipCreate.stderr.on('data',function(data){
s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()})
})
zipCreate.on('exit',function(data){
fs.unlinkSync(tempScript)
tempFiles.forEach(function(file){
fs.unlink(file,function(){})
})
if(zipDownload && zipDownload.destroy){
res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"')
var zipDownload = fs.createReadStream(zippedFile)
zipDownload.pipe(res)
zipDownload.on('error', function (error) {
var errorString = error.toString()
s.userLog({
ke: req.params.ke,
mid: '$USER'
},{
title: 'Zip Download Error',
msg: errorString
})
if(zipDownload && zipDownload.destroy){
zipDownload.destroy()
}
res.end(s.prettyPrint({
ok: false,
msg: errorString
}))
})
zipDownload.on('close', function () {
res.end()
zipDownload.destroy()
}
res.end(s.prettyPrint({
ok: false,
msg: errorString
}))
})
zipDownload.on('close', function () {
res.end()
zipDownload.destroy()
fs.unlinkSync(zippedFile)
fs.unlinkSync(zippedFile)
})
})
})
})
@ -1662,11 +1672,13 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND mid=? AND `time`=? LIMIT 1',[req.params.ke,req.params.id,time],function(err,r){
if(r&&r[0]){
req.dir=s.getVideoDirectory(r[0])+req.params.file
if (fs.existsSync(req.dir)){
s.streamMp4FileOverHttp(req.dir,req,res)
}else{
res.end(user.lang['File Not Found in Filesystem'])
}
fs.stat(req.dir,function(err,stats){
if (!err){
s.streamMp4FileOverHttp(req.dir,req,res)
}else{
res.end(user.lang['File Not Found in Filesystem'])
}
})
}else{
res.end(user.lang['File Not Found in Database'])
}
@ -1678,30 +1690,76 @@ module.exports = function(s,config,lang,app,io){
*/
app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){
s.auth(req.params,function(user){
var endData = {
}
if(req.query.data){
try{
var d = {
id: req.params.id,
ke: req.params.ke,
details: JSON.parse(req.query.data)
details: s.parseJSON(req.query.data)
}
}catch(err){
res.end('Data Broken',err)
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['Data Broken']
})
return
}
}else{
res.end('No Data')
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['No Data']
})
return
}
if(!d.ke||!d.id||!s.group[d.ke]){
res.end(user.lang['No Group with this key exists'])
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['No Group with this key exists']
})
return
}
if(!s.group[d.ke].rawMonitorConfigurations[d.id]){
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['Monitor or Key does not exist.']
})
return
}
var details = s.group[d.ke].rawMonitorConfigurations[d.id].details
var detectorHttpApi = details.detector_http_api
var detectorOn = (details.detector === '1')
switch(detectorHttpApi){
case'0':
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['Trigger Blocked']
})
return
break;
case'2':
if(!detectorOn){
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['Trigger Blocked']
})
return
}
break;
case'2':
if(detectorOn){
s.closeJsonResponse(res,{
ok: false,
msg: user.lang['Trigger Blocked']
})
return
}
break;
}
s.triggerEvent(d)
res.end(user.lang['Trigger Successful'])
s.closeJsonResponse(res,{
ok: true,
msg: user.lang['Trigger Successful']
})
},res,req)
})
/**
@ -1809,13 +1867,13 @@ module.exports = function(s,config,lang,app,io){
var checkOrigin = function(search){return req.headers.host.indexOf(search)>-1}
if(checkOrigin('127.0.0.1')){
if(!req.params.feed){req.params.feed='1'}
if(!s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed]){
s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed] = new events.EventEmitter().setMaxListeners(0)
if(!s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed]){
s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed] = new events.EventEmitter().setMaxListeners(0)
}
//req.params.feed = Feed Number
res.connection.setTimeout(0);
req.on('data', function(buffer){
s.group[req.params.ke].mon[req.params.id].streamIn[req.params.feed].emit('data',buffer)
s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.params.feed].emit('data',buffer)
});
req.on('end',function(){
// console.log('streamIn closed',req.params);
@ -1873,7 +1931,10 @@ module.exports = function(s,config,lang,app,io){
/**
* API : ONVIF Method Controller
*/
app.all([config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'],function (req,res){
app.all([
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'
],function (req,res){
var response = {ok:false};
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
@ -1904,7 +1965,7 @@ module.exports = function(s,config,lang,app,io){
var completeAction = function(command){
if(command.then){
command.then(actionCallback).catch(function(error){
errorMessage('Device responded with an error',error)
errorMessage('Device Action responded with an error',error)
})
}else if(command){
response.ok = true
@ -1927,6 +1988,7 @@ module.exports = function(s,config,lang,app,io){
}else{
action = Camera[req.params.action]
}
console.log(s.parseJSON(req.query.options))
if(!action || typeof action !== 'function'){
errorMessage(req.params.action+' is not an available ONVIF function. See https://github.com/futomi/node-onvif for functions.')
}else{
@ -1970,10 +2032,10 @@ module.exports = function(s,config,lang,app,io){
completeAction(command)
}
}
if(!s.group[req.params.ke].mon[req.params.id].onvifConnection){
if(!s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection){
//prepeare onvif connection
var controlURL
var monitorConfig = s.group[req.params.ke].mon_conf[req.params.id]
var monitorConfig = s.group[req.params.ke].rawMonitorConfigurations[req.params.id]
if(!monitorConfig.details.control_base_url||monitorConfig.details.control_base_url===''){
controlURL = s.buildMonitorUrl(monitorConfig, true)
}else{
@ -1981,19 +2043,19 @@ module.exports = function(s,config,lang,app,io){
}
var controlURLOptions = s.cameraControlOptionsFromUrl(controlURL,monitorConfig)
//create onvif connection
s.group[req.params.ke].mon[req.params.id].onvifConnection = new onvif.OnvifDevice({
s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection = new onvif.OnvifDevice({
xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service',
user : controlURLOptions.username,
pass : controlURLOptions.password
})
var device = s.group[req.params.ke].mon[req.params.id].onvifConnection
var device = s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection
device.init().then((info) => {
if(info)doAction(device)
}).catch(function(error){
return errorMessage('Device responded with an error',error)
})
}else{
doAction(s.group[req.params.ke].mon[req.params.id].onvifConnection)
doAction(s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection)
}
},res,req);
})
@ -2021,6 +2083,30 @@ module.exports = function(s,config,lang,app,io){
},res,req)
})
/**
* API : Get Definitions JSON
*/
app.get(config.webPaths.apiPrefix+':auth/definitions/:ke',function (req,res){
s.auth(req.params,function(user){
var endData = {
ok: true,
definitions: s.getDefinitonFile(user.details.lang)
}
s.closeJsonResponse(res,endData)
},res,req)
})
/**
* API : Get Language JSON
*/
app.get(config.webPaths.apiPrefix+':auth/language/:ke',function (req,res){
s.auth(req.params,function(user){
var endData = {
ok: true,
definitions: s.getLanguageFile(user.details.lang)
}
s.closeJsonResponse(res,endData)
},res,req)
})
/**
* Robots.txt
*/
app.get('/robots.txt', function (req,res){

View file

@ -10,7 +10,6 @@ var spawn = require('child_process').spawn;
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){
/**
* Page : Get Embed Stream
@ -22,10 +21,10 @@ module.exports = function(s,config,lang,app){
res.end(user.lang['Not Permitted'])
return
}
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){
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[req.params.id]){
if(s.group[req.params.ke].activeMonitors[req.params.id].isStarted === true){
req.params.uid=user.uid;
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)});
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config: s.getConfigWithBranding(req.hostname),lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].rawMonitorConfigurations[req.params.id])),originalURL:s.getOriginalUrl(req)});
res.end()
}else{
res.end(user.lang['Cannot watch a monitor that isn\'t running.'])
@ -40,7 +39,7 @@ module.exports = function(s,config,lang,app){
*/
app.get([config.webPaths.apiPrefix+':auth/mp4/:ke/:id/:channel/s.mp4',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/s.mp4',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/:channel/s.ts',config.webPaths.apiPrefix+':auth/mp4/:ke/:id/s.ts'], function (req, res) {
s.auth(req.params,function(user){
if(!s.group[req.params.ke] || !s.group[req.params.ke].mon[req.params.id]){
if(!s.group[req.params.ke] || !s.group[req.params.ke].activeMonitors[req.params.id]){
res.status(404);
res.end('404 : Monitor not found');
return
@ -50,7 +49,7 @@ module.exports = function(s,config,lang,app){
if(req.params.channel){
Channel = parseInt(req.params.channel)+config.pipeAddition
}
var mp4frag = s.group[req.params.ke].mon[req.params.id].mp4frag[Channel];
var mp4frag = s.group[req.params.ke].activeMonitors[req.params.id].mp4frag[Channel];
var errorMessage = 'MP4 Stream is not enabled'
if(!mp4frag){
res.status(503);
@ -105,7 +104,7 @@ module.exports = function(s,config,lang,app){
}else{
s.auth(req.params,function(user){
s.checkChildProxy(req.params,function(){
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[req.params.id]){
if(s.group[req.params.ke]&&s.group[req.params.ke].activeMonitors[req.params.id]){
if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
res.end(user.lang['Not Permitted'])
return
@ -113,9 +112,9 @@ module.exports = function(s,config,lang,app){
var Emitter
if(!req.params.channel){
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
}else{
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
}
res.writeHead(200, {
'Content-Type': 'multipart/x-mixed-replace; boundary=shinobi',
@ -123,33 +122,35 @@ module.exports = function(s,config,lang,app){
'Connection': 'keep-alive',
'Pragma': 'no-cache'
});
var contentWriter,content = fs.readFileSync(config.defaultMjpeg,'binary');
res.write("--shinobi\r\n");
res.write("Content-Type: image/jpeg\r\n");
res.write("Content-Length: " + content.length + "\r\n");
res.write("\r\n");
res.write(content,'binary');
res.write("\r\n");
var ip = s.getClientIp(req)
s.camera('watch_on',{
id : req.params.id,
ke : req.params.ke
},{
id : req.params.auth + ip + req.headers['user-agent']
})
Emitter.on('data',contentWriter=function(d){
content = d;
var contentWriter
fs.readFile(config.defaultMjpeg,'binary',function(err,content){
res.write("--shinobi\r\n");
res.write("Content-Type: image/jpeg\r\n");
res.write("Content-Length: " + content.length + "\r\n");
res.write("\r\n");
res.write(content,'binary');
})
res.on('close', function () {
Emitter.removeListener('data',contentWriter)
s.camera('watch_off',{
res.write("\r\n");
var ip = s.getClientIp(req)
s.camera('watch_on',{
id : req.params.id,
ke : req.params.ke
},{
id : req.params.auth + ip + req.headers['user-agent']
})
});
Emitter.on('data',contentWriter=function(d){
content = d;
res.write(content,'binary');
})
res.on('close', function () {
Emitter.removeListener('data',contentWriter)
s.camera('watch_off',{
id : req.params.id,
ke : req.params.ke
},{
id : req.params.auth + ip + req.headers['user-agent']
})
})
})
}else{
res.end();
}
@ -212,20 +213,20 @@ module.exports = function(s,config,lang,app){
s.checkChildProxy(req.params,function(){
var Emitter,chunkChannel
if(!req.params.channel){
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
chunkChannel = parseInt(req.params.channel)+config.pipeAddition
}
if(s.group[req.params.ke].mon[req.params.id].firstStreamChunk[chunkChannel]){
if(s.group[req.params.ke].activeMonitors[req.params.id].firstStreamChunk[chunkChannel]){
//variable name of contentWriter
var contentWriter
//set headers
res.setHeader('Content-Type', 'video/x-flv');
res.setHeader('Access-Control-Allow-Origin','*');
//write first frame on stream
res.write(s.group[req.params.ke].mon[req.params.id].firstStreamChunk[chunkChannel])
res.write(s.group[req.params.ke].activeMonitors[req.params.id].firstStreamChunk[chunkChannel])
var ip = s.getClientIp(req)
s.camera('watch_on',{
id : req.params.id,
@ -262,10 +263,10 @@ module.exports = function(s,config,lang,app){
s.checkChildProxy(req.params,function(){
var Emitter,chunkChannel
if(!req.params.channel){
Emitter = s.group[req.params.ke].mon[req.params.id].emitter
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitter
chunkChannel = 'MAIN'
}else{
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
chunkChannel = parseInt(req.params.channel)+config.pipeAddition
}
//variable name of contentWriter
@ -312,9 +313,9 @@ module.exports = function(s,config,lang,app){
if(!req.query.feed){req.query.feed='1'}
var Emitter
if(!req.params.feed){
Emitter = s.group[req.params.ke].mon[req.params.id].streamIn[req.query.feed]
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].streamIn[req.query.feed]
}else{
Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.feed)+config.pipeAddition]
Emitter = s.group[req.params.ke].activeMonitors[req.params.id].emitterChannel[parseInt(req.params.feed)+config.pipeAddition]
}
var contentWriter
var date = new Date();

View file

@ -634,4 +634,42 @@ module.exports = function(s,config,lang,app){
res.end(s.prettyPrint(endData))
},res,req)
})
/**
* API : Superuser : Get Child Nodes
*/
app.all(config.webPaths.superApiPrefix+':auth/getChildNodes', function (req,res){
s.superAuth(req.params,function(resp){
var childNodesJson = {}
Object.values(s.childNodes).forEach(function(activeNode){
var activeCamerasCount = 0
var activeCameras = {}
Object.values(activeNode.activeCameras).forEach(function(monitor){
++activeCamerasCount
if(!activeCameras[monitor.ke])activeCameras[monitor.ke] = {}
activeCameras[monitor.ke][monitor.mid] = {
name: monitor.name,
mid: monitor.mid,
ke: monitor.ke,
details: {
accelerator: monitor.details.accelerator,
auto_host: monitor.details.auto_host,
}
}
})
childNodesJson[activeNode.ip] = {
ip: activeNode.ip,
cpu: activeNode.cpu,
dead: activeNode.dead,
countCount: activeNode.countCount,
activeMonitorsCount: activeCamerasCount,
activeMonitors: activeCameras,
}
})
var endData = {
ok : true,
childNodes: childNodesJson,
}
res.end(s.prettyPrint(endData))
},res,req)
})
}