shinobi
Version:
CCTV and NVR for Node.js
992 lines (987 loc) • 178 kB
JavaScript
//
// Shinobi
// Copyright (C) 2016 Moe Alam, moeiscool
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// # Donate
//
// If you like what I am doing here and want me to continue please consider donating :)
// PayPal : paypal@m03.ca
//
process.on('uncaughtException', function (err) {
console.error('uncaughtException',err);
});
var fs = require('fs');
var os = require('os');
var URL = require('url');
var path = require('path');
var mysql = require('mysql');
var moment = require('moment');
var request = require("request");
var express = require('express');
var app = express();
var appHTTPS = express();
var http = require('http');
var https = require('https');
var server = http.createServer(app);
var bodyParser = require('body-parser');
var CircularJSON = require('circular-json');
var ejs = require('ejs');
var io = new (require('socket.io'))();
var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var crypto = require('crypto');
var webdav = require("webdav");
var connectionTester = require('connection-tester');
var events = require('events');
var df = require('node-df');
var Cam = require('onvif').Cam;
var config = require('./conf.json');
if(!config.language){
config.language='en_CA'
}
if(config.language.split('_')[0]==='he'){config.language=='ar'}
try{
var lang = require('./languages/'+config.language+'.json');
}catch(er){
console.error(er)
console.log('There was an error loading your language file.')
var lang = require('./languages/en_CA.json');
}
process.send = process.send || function () {};
if(config.mail){
var nodemailer = require('nodemailer').createTransport(config.mail);
}
//config defaults
if(config.cpuUsageMarker===undefined){config.cpuUsageMarker='%Cpu'}
if(config.autoDropCache===undefined){config.autoDropCache=true}
if(config.doSnapshot===undefined){config.doSnapshot=true}
if(config.restart===undefined){config.restart={}}
if(config.systemLog===undefined){config.systemLog=true}
if(config.deleteCorruptFiles===undefined){config.deleteCorruptFiles=true}
if(config.restart.onVideoNotExist===undefined){config.restart.onVideoNotExist=true}
if(config.ip===undefined||config.ip===''||config.ip.indexOf('0.0.0.0')>-1){config.ip='localhost'}else{config.bindip=config.ip};
if(config.cron===undefined)config.cron={};
if(config.cron.deleteOverMax===undefined)config.cron.deleteOverMax=true;
if(config.cron.deleteOverMaxOffset===undefined)config.cron.deleteOverMaxOffset=0.9;
if(config.pluginKeys===undefined)config.pluginKeys={};
s={factorAuth:{},child_help:false,totalmem:os.totalmem(),platform:os.platform(),s:JSON.stringify,isWin:(process.platform==='win32')};
s.loadedLanguages={}
s.loadedLanguages[config.language]=lang;
s.getLanguageFile=function(rule,file){
if(rule&&rule!==''){
file=s.loadedLanguages[file]
if(!file){
try{
s.loadedLanguages[rule]=require('./languages/'+rule+'.json')
file=s.loadedLanguages[rule]
}catch(err){
file=lang
}
}
}else{
file=lang
}
return file
}
s.systemLog=function(q,w,e){
if(!w){w=''}
if(!e){e=''}
if(config.systemLog===true){
return console.log(moment().format(),q,w,e)
}
}
s.disc=function(){
sql = mysql.createConnection(config.db);
sql.connect(function(err){if(err){s.systemLog(lang['Error Connecting']+' : DB',err);setTimeout(s.disc, 2000);}});
sql.on('error',function(err) {s.systemLog(lang['DB Lost.. Retrying..']);s.systemLog(err);s.disc();return;});
}
s.disc();
//kill any ffmpeg running
s.ffmpegKill=function(){exec("ps aux | grep -ie ffmpeg | awk '{print $2}' | xargs kill -9",{detached: true})};
process.on('exit',s.ffmpegKill.bind(null,{cleanup:true}));
process.on('SIGINT',s.ffmpegKill.bind(null, {exit:true}));
//key for child servers
s.child_nodes={};
s.child_key='3123asdasdf1dtj1hjk23sdfaasd12asdasddfdbtnkkfgvesra3asdsd3123afdsfqw345';
s.checkRelativePath=function(x){
if(x.charAt(0)!=='/'){
x=__dirname+'/'+x
}
return x
}
s.checkCorrectPathEnding=function(x){
var length=x.length
if(x.charAt(length-1)!=='/'){
x=x+'/'
}
return x
}
s.md5=function(x){return crypto.createHash('md5').update(x).digest("hex");}
s.tx=function(z,y,x){if(x){return x.broadcast.to(y).emit('f',z)};io.to(y).emit('f',z);}
s.cx=function(z,y,x){if(x){return x.broadcast.to(y).emit('c',z)};io.to(y).emit('c',z);}
s.txWithSubPermissions=function(z,y,permissionChoices){
if(typeof permissionChoices==='string'){
permissionChoices=[permissionChoices]
}
if(s.group[z.ke]){
Object.keys(s.group[z.ke].users).forEach(function(v){
var user = s.group[z.ke].users[v]
if(user.details.sub){
if(user.details.allmonitors!=='1'){
var valid=0
var checked=permissionChoices.length
permissionChoices.forEach(function(b){
if(user.details[b].indexOf(z.mid)!==-1){
++valid
}
})
if(valid===checked){
s.tx(z,user.cnid)
}
}else{
s.tx(z,user.cnid)
}
}else{
s.tx(z,user.cnid)
}
})
}
}
//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.ratio=function(width,height,ratio){ratio = width / height;return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';}
s.gid=function(x){
if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < x; i++ )
t += p.charAt(Math.floor(Math.random() * p.length));
return t;
};
s.nid=function(x){
if(!x){x=6};var t = "";var p = "0123456789";
for( var i=0; i < x; i++ )
t += p.charAt(Math.floor(Math.random() * p.length));
return t;
};
s.moment_withOffset=function(e,x){
if(!e){e=new Date};if(!x){x='YYYY-MM-DDTHH-mm-ss'};
e=moment(e);if(config.utcOffset){e=e.utcOffset(config.utcOffset)}
return e.format(x);
}
s.moment=function(e,x){
if(!e){e=new Date};if(!x){x='YYYY-MM-DDTHH-mm-ss'};
return moment(e).format(x);
}
s.ipRange=function(start_ip, end_ip) {
var start_long = s.toLong(start_ip);
var end_long = s.toLong(end_ip);
if (start_long > end_long) {
var tmp=start_long;
start_long=end_long
end_long=tmp;
}
var range_array = [];
var i;
for (i=start_long; i<=end_long;i++) {
range_array.push(s.fromLong(i));
}
return range_array;
}
s.portRange=function(lowEnd,highEnd){
var list = [];
for (var i = lowEnd; i <= highEnd; i++) {
list.push(i);
}
return list;
}
//toLong taken from NPM package 'ip'
s.toLong=function(ip) {
var ipl = 0;
ip.split('.').forEach(function(octet) {
ipl <<= 8;
ipl += parseInt(octet);
});
return(ipl >>> 0);
};
//fromLong taken from NPM package 'ip'
s.fromLong=function(ipl) {
return ((ipl >>> 24) + '.' +
(ipl >> 16 & 255) + '.' +
(ipl >> 8 & 255) + '.' +
(ipl & 255) );
};
s.kill=function(x,e,p){
if(s.group[e.ke]&&s.group[e.ke].mon[e.id]&&s.group[e.ke].mon[e.id].spawn !== undefined){
if(s.group[e.ke].mon[e.id].spawn){
try{
s.group[e.ke].mon[e.id].spawn.removeListener('end',s.group[e.ke].mon[e.id].spawn_exit);
s.group[e.ke].mon[e.id].spawn.removeListener('exit',s.group[e.ke].mon[e.id].spawn_exit);
delete(s.group[e.ke].mon[e.id].spawn_exit);
}catch(er){}
}
clearTimeout(s.group[e.ke].mon[e.id].checker);
delete(s.group[e.ke].mon[e.id].checker);
clearTimeout(s.group[e.ke].mon[e.id].watchdog_stop);
delete(s.group[e.ke].mon[e.id].watchdog_stop);
if(e&&s.group[e.ke].mon[e.id].record){
clearTimeout(s.group[e.ke].mon[e.id].record.capturing);
// if(s.group[e.ke].mon[e.id].record.request){s.group[e.ke].mon[e.id].record.request.abort();delete(s.group[e.ke].mon[e.id].record.request);}
};
if(s.group[e.ke].mon[e.id].child_node){
s.cx({f:'kill',d:s.init('noReference',e)},s.group[e.ke].mon[e.id].child_node_id)
}else{
if(!x||x===1){return};
p=x.pid;
if(s.group[e.ke].mon_conf[e.id].type===('dashcam'||'socket'||'jpeg'||'pipe')){
x.stdin.pause();setTimeout(function(){x.kill('SIGTERM');delete(x);},500)
}else{
try{
x.stdin.setEncoding('utf8');x.stdin.write('q');
}catch(er){}
}
setTimeout(function(){exec('kill -9 '+p,{detached: true})},1000)
}
}
}
s.log=function(e,x){
if(!x||!e.mid){return}
if(e.details&&e.details.sqllog==1){
sql.query('INSERT INTO Logs (ke,mid,info) VALUES (?,?,?)',[e.ke,e.mid,s.s(x)]);
}
s.tx({f:'log',ke:e.ke,mid:e.mid,log:x,time:moment()},'GRP_'+e.ke);
// s.systemLog('s.log : ',{f:'log',ke:e.ke,mid:e.mid,log:x,time:moment()},'GRP_'+e.ke)
}
//SSL options
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')
if(config.ssl.port===undefined){
config.ssl.port=443
}
if(config.ssl.bindip===undefined){
config.ssl.bindip=config.bindip
}
if(config.ssl.ca&&config.ssl.ca instanceof Array){
config.ssl.ca.forEach(function(v,n){
config.ssl.ca[n]=fs.readFileSync(s.checkRelativePath(v),'utf8')
})
}
var serverHTTPS = https.createServer(config.ssl,app);
serverHTTPS.listen(config.ssl.port,config.bindip,function(){
console.log('SSL '+lang.Shinobi+' - SSL PORT : '+config.ssl.port);
});
io.attach(serverHTTPS);
}
//start HTTP
server.listen(config.port,config.bindip,function(){
console.log(lang.Shinobi+' - PORT : '+config.port);
});
io.attach(server);
console.log('NODE.JS : '+execSync("node -v"))
//ffmpeg location
if(!config.ffmpegDir){
if(s.isWin===true){
config.ffmpegDir=__dirname+'/ffmpeg/ffmpeg.exe'
}else{
config.ffmpegDir='ffmpeg'
}
}
//directories
s.group={};
if(!config.windowsTempDir&&s.isWin===true){config.windowsTempDir='C:/Windows/Temp'}
if(!config.defaultMjpeg){config.defaultMjpeg=__dirname+'/web/libs/img/bg.jpg'}
//default stream folder check
if(!config.streamDir){
if(s.isWin===false){
config.streamDir='/dev/shm'
}else{
config.streamDir=config.windowsTempDir
}
if(!fs.existsSync(config.streamDir)){
config.streamDir=__dirname+'/streams/'
}else{
config.streamDir+='/streams/'
}
}
if(!config.videosDir){config.videosDir=__dirname+'/videos/'}
s.dir={videos:config.videosDir,streams:config.streamDir,languages:'./languages/'};
//streams dir
if(!fs.existsSync(s.dir.streams)){
fs.mkdirSync(s.dir.streams);
}
//videos dir
if(!fs.existsSync(s.dir.videos)){
fs.mkdirSync(s.dir.videos);
}
////Camera Controller
s.init=function(x,e,k,fn){
if(!e){e={}}
if(!k){k={}}
switch(x){
case 0://camera
if(!s.group[e.ke]){s.group[e.ke]={}};
if(!s.group[e.ke].mon){s.group[e.ke].mon={}}
if(!s.group[e.ke].users){s.group[e.ke].users={}}
if(!s.group[e.ke].mon[e.mid]){s.group[e.ke].mon[e.mid]={}}
if(!s.group[e.ke].mon[e.mid].watch){s.group[e.ke].mon[e.mid].watch={}};
if(!s.group[e.ke].mon[e.mid].fixingVideos){s.group[e.ke].mon[e.mid].fixingVideos={}};
if(!s.group[e.ke].mon[e.mid].record){s.group[e.ke].mon[e.mid].record={yes:e.record}};
if(!s.group[e.ke].mon[e.mid].started){s.group[e.ke].mon[e.mid].started=0};
if(s.group[e.ke].mon[e.mid].delete){clearTimeout(s.group[e.ke].mon[e.mid].delete)}
if(!s.group[e.ke].mon_conf){s.group[e.ke].mon_conf={}}
s.init('apps',e)
break;
case'apps':
if(!s.group[e.ke].init){
s.group[e.ke].init={};
}
if(!s.group[e.ke].webdav||!s.group[e.ke].init.size){
sql.query('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);
//owncloud/webdav
if(ar.webdav_user&&
ar.webdav_user!==''&&
ar.webdav_pass&&
ar.webdav_pass!==''&&
ar.webdav_url&&
ar.webdav_url!==''
){
if(!ar.webdav_dir||ar.webdav_dir===''){
ar.webdav_dir='/';
if(ar.webdav_dir.slice(-1)!=='/'){ar.webdav_dir+='/';}
}
s.group[e.ke].webdav = webdav(
ar.webdav_url,
ar.webdav_user,
ar.webdav_pass
);
}
Object.keys(ar).forEach(function(v){
s.group[e.ke].init[v]=ar[v]
})
}
});
}
break;
case'sync':
e.cn=Object.keys(s.child_nodes);
e.cn.forEach(function(v){
if(s.group[e.ke]){
s.cx({f:'sync',sync:s.init('noReference',s.group[e.ke].mon[e.mid]),ke:e.ke,mid:e.mid},s.child_nodes[v].cnid);
}
});
break;
case'noReference':
x={keys:Object.keys(e),ar:{}};
x.keys.forEach(function(v){
if(v!=='last_frame'&&v!=='record'&&v!=='spawn'&&v!=='running'&&(v!=='time'&&typeof e[v]!=='function')){x.ar[v]=e[v];}
});
return x.ar;
break;
case'url':
e.authd='';
if(e.details.muser&&e.details.muser!==''&&e.host.indexOf('@')===-1) {
e.authd=e.details.muser+':'+e.details.mpass+'@';
}
if(e.port==80&&e.details.port_force!=='1'){e.porty=''}else{e.porty=':'+e.port}
e.url=e.protocol+'://'+e.authd+e.host+e.porty+e.path;return e.url;
break;
case'url_no_path':
e.authd='';
if(!e.details.muser){e.details.muser=''}
if(!e.details.mpass){e.details.mpass=''}
if(e.details.muser!==''&&e.host.indexOf('@')===-1) {
e.authd=e.details.muser+':'+e.details.mpass+'@';
}
if(e.port==80&&e.details.port_force!=='1'){e.porty=''}else{e.porty=':'+e.port}
e.url=e.protocol+'://'+e.authd+e.host+e.porty;return e.url;
break;
case'diskUsed':
if(s.group[e.ke]&&s.group[e.ke].init){
s.tx({f:'diskUsed',size:s.group[e.ke].init.used_space,limit:s.group[e.ke].init.size},'GRP_'+e.ke);
}
break;
}
if(typeof e.callback==='function'){setTimeout(function(){e.callback()},500);}
}
s.filter=function(x,d){
switch(x){
case'archive':
d.videos.forEach(function(v,n){
s.video('archive',v)
})
break;
case'email':
if(d.videos&&d.videos.length>0){
d.videos.forEach(function(v,n){
})
d.mailOptions = {
from: '"ShinobiCCTV" <no-reply@shinobi.video>', // sender address
to: d.mail, // list of receivers
subject: lang['Filter Matches']+' : '+d.name, // Subject line
html: lang.FilterMatchesText1+' '+d.videos.length+' '+lang.FilterMatchesText2,
};
if(d.execute&&d.execute!==''){
d.mailOptions.html+='<div><b>'+lang.Executed+' :</b> '+d.execute+'</div>'
}
if(d.delete==='1'){
d.mailOptions.html+='<div><b>'+lang.Deleted+' :</b> '+lang.Yes+'</div>'
}
d.mailOptions.html+='<div><b>'+lang.Query+' :</b> '+d.query+'</div>'
d.mailOptions.html+='<div><b>'+lang['Filter ID']+' :</b> '+d.id+'</div>'
nodemailer.sendMail(d.mailOptions, (error, info) => {
if (error) {
s.tx({f:'error',ff:'filter_mail',ke:d.ke,error:error},'GRP_'+d.ke);
return ;
}
s.tx({f:'filter_mail',ke:d.ke,info:info},'GRP_'+d.ke);
});
}
break;
case'delete':
d.videos.forEach(function(v,n){
s.video('delete',v)
})
break;
case'execute':
exec(d.execute,{detached: true})
break;
}
}
s.video=function(x,e){
if(!e){e={}};
k={}
if(e.mid&&!e.id){e.id=e.mid};
switch(x){
case'fix':
e.dir=s.dir.videos+e.ke+'/'+e.id+'/';
e.sdir=s.dir.streams+e.ke+'/'+e.id+'/';
if(!e.filename&&e.time){e.filename=s.moment(e.time)}
if(e.filename.indexOf('.')===-1){
e.filename=e.filename+'.'+e.ext
}
s.tx({f:'video_fix_start',mid:e.mid,ke:e.ke,filename:e.filename},'GRP_'+e.ke)
s.group[e.ke].mon[e.id].fixingVideos[e.filename]={}
switch(e.ext){
case'mp4':
e.fixFlags='-vcodec libx264 -acodec aac -strict -2';
break;
case'webm':
e.fixFlags='-vcodec libvpx -acodec libvorbis';
break;
}
e.spawn=spawn(config.ffmpegDir,('-i '+e.dir+e.filename+' '+e.fixFlags+' '+e.sdir+e.filename).split(' '),{detached: true})
e.spawn.stdout.on('data',function(data){
s.tx({f:'video_fix_data',mid:e.mid,ke:e.ke,filename:e.filename},'GRP_'+e.ke)
});
e.spawn.on('close',function(data){
exec('mv '+e.dir+e.filename+' '+e.sdir+e.filename,{detached: true}).on('exit',function(){
s.tx({f:'video_fix_success',mid:e.mid,ke:e.ke,filename:e.filename},'GRP_'+e.ke)
delete(s.group[e.ke].mon[e.id].fixingVideos[e.filename]);
})
});
break;
case'archive':
e.dir=s.dir.videos+e.ke+'/'+e.id+'/';
if(!e.filename&&e.time){e.filename=s.moment(e.time)}
if(!e.status){e.status=0}
e.save=[e.id,e.ke,s.nameToTime(e.filename)];
sql.query('UPDATE Videos SET status=3 WHERE `mid`=? AND `ke`=? AND `time`=?',e.save,function(err,r){
s.tx({f:'video_edit',status:3,filename:e.filename+'.'+e.ext,mid:e.mid,ke:e.ke,time:s.nameToTime(e.filename)},'GRP_'+e.ke);
});
break;
case'delete':
e.dir=s.dir.videos+e.ke+'/'+e.id+'/';
if(!e.filename&&e.time){e.filename=s.moment(e.time)}
if(!e.status){e.status=0}
e.save=[e.id,e.ke,s.nameToTime(e.filename)];
sql.query('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',e.save,function(err,r){
fs.stat(e.dir+e.filename+'.'+e.ext,function(err,file){
if(err){
return s.systemLog(err)
}
s.group[e.ke].init.used_space=s.group[e.ke].init.used_space-(file.size/1000000)
s.init('diskUsed',e)
})
s.tx({f:'video_delete',filename:e.filename+'.'+e.ext,mid:e.mid,ke:e.ke,time:s.nameToTime(e.filename),end:s.moment(new Date,'YYYY-MM-DD HH:mm:ss')},'GRP_'+e.ke);
s.file('delete',e.dir+e.filename+'.'+e.ext)
})
break;
case'open':
e.save=[e.id,e.ke,s.nameToTime(e.filename),e.ext];
if(!e.status){e.save.push(0)}else{e.save.push(e.status)}
sql.query('INSERT INTO Videos (mid,ke,time,ext,status) VALUES (?,?,?,?,?)',e.save)
s.tx({f:'video_build_start',filename:e.filename+'.'+e.ext,mid:e.id,ke:e.ke,time:s.nameToTime(e.filename),end:s.moment(new Date,'YYYY-MM-DD HH:mm:ss')},'GRP_'+e.ke);
break;
case'close':
e.dir=s.dir.videos+e.ke+'/'+e.id+'/';
if(s.group[e.ke]&&s.group[e.ke].mon[e.id]){
if(s.group[e.ke].mon[e.id].open&&!e.filename){e.filename=s.group[e.ke].mon[e.id].open;e.ext=s.group[e.ke].mon[e.id].open_ext}
if(s.group[e.ke].mon[e.id].child_node){
s.cx({f:'close',d:s.init('noReference',e)},s.group[e.ke].mon[e.id].child_node_id);
}else{
if(fs.existsSync(e.dir+e.filename+'.'+e.ext)===true){
k.stat=fs.statSync(e.dir+e.filename+'.'+e.ext);
e.filesize=k.stat.size;
e.filesizeMB=parseFloat((e.filesize/1000000).toFixed(2));
e.end_time=s.moment(k.stat.mtime,'YYYY-MM-DD HH:mm:ss');
if(config.deleteCorruptFiles===true&&e.filesizeMB<0.05){
s.video('delete',e);
s.log(e,{type:'File Corrupt',msg:{ffmpeg:s.group[e.ke].mon[e.mid].ffmpeg,filesize:e.filesizeMB}})
}else{
e.save=[e.filesize,1,e.end_time,e.id,e.ke,s.nameToTime(e.filename)];
if(!e.status){e.save.push(0)}else{e.save.push(e.status)}
sql.query('UPDATE Videos SET `size`=?,`status`=?,`end`=? WHERE `mid`=? AND `ke`=? AND `time`=? AND `status`=?',e.save)
s.txWithSubPermissions({f:'video_build_success',hrefNoAuth:'/videos/'+e.ke+'/'+e.mid+'/'+e.filename+'.'+e.ext,filename:e.filename+'.'+e.ext,mid:e.id,ke:e.ke,time:moment(s.nameToTime(e.filename)).format(),size:e.filesize,end:moment(e.end_time).format()},'GRP_'+e.ke,'video_view');
//cloud auto savers
//webdav
if(s.group[e.ke].webdav&&s.group[e.ke].init.use_webdav!=='0'&&s.group[e.ke].init.webdav_save=="1"){
fs.readFile(e.dir+e.filename+'.'+e.ext,function(err,data){
s.group[e.ke].webdav.putFileContents(s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/'+e.filename+'.'+e.ext,"binary",data)
.catch(function(err) {
s.log(e,{type:lang['Webdav Error'],msg:{msg:lang.WebdavErrorText+' <b>/'+e.ke+'/'+e.id+'</b>',info:err},ffmpeg:s.group[e.ke].mon[e.id].ffmpeg})
console.error(err);
});
});
}
if(s.group[e.ke].init){
if(!s.group[e.ke].init.used_space){s.group[e.ke].init.used_space=0}else{s.group[e.ke].init.used_space=parseFloat(s.group[e.ke].init.used_space)}
s.group[e.ke].init.used_space=s.group[e.ke].init.used_space+e.filesizeMB;
if(config.cron.deleteOverMax===true&&s.group[e.ke].checkSpaceLock!==1){
//check space
var check=function(){
if(s.group[e.ke].init.used_space>(s.group[e.ke].init.size*config.cron.deleteOverMaxOffset)){
s.group[e.ke].checkSpaceLock=1;
sql.query('SELECT * FROM Videos WHERE status != 0 AND ke=? ORDER BY `time` ASC LIMIT 2',[e.ke],function(err,evs){
k.del=[];k.ar=[e.ke];
evs.forEach(function(ev){
ev.dir=s.dir.videos+e.ke+'/'+ev.mid+'/'+s.moment(ev.time)+'.'+ev.ext;
k.del.push('(mid=? AND time=?)');
k.ar.push(ev.mid),k.ar.push(ev.time);
s.file('delete',ev.dir);
s.group[e.ke].init.used_space=s.group[e.ke].init.used_space-ev.size/1000000;
s.tx({f:'video_delete',ff:'over_max',size:s.group[e.ke].init.used_space,limit:s.group[e.ke].init.size,filename:s.moment(ev.time)+'.'+ev.ext,mid:ev.mid,ke:ev.ke,time:ev.time,end:s.moment(new Date,'YYYY-MM-DD HH:mm:ss')},'GRP_'+e.ke);
});
if(k.del.length>0){
k.qu=k.del.join(' OR ');
sql.query('DELETE FROM Videos WHERE ke =? AND ('+k.qu+')',k.ar,function(){
check()
})
}
})
}else{
s.group[e.ke].checkSpaceLock=0
s.init('diskUsed',e)
}
}
check()
}else{
s.init('diskUsed',e)
}
}
}
}else{
s.video('delete',e);
s.log(e,{type:lang['File Not Exist'],msg:lang.FileNotExistText,ffmpeg:s.group[e.ke].mon[e.id].ffmpeg})
if(e.mode&&config.restart.onVideoNotExist===true&&e.fn){
delete(s.group[e.ke].mon[e.id].open);
s.log(e,{type:lang['Camera is not recording'],msg:{msg:lang.CameraNotRecordingText}});
if(s.group[e.ke].mon[e.id].started===1){
s.camera('restart',e)
}
}
}
}
}
delete(s.group[e.ke].mon[e.id].open);
break;
}
}
s.ffmpeg=function(e,x){
//set X for temporary values so we don't break our main monitor object.
if(!x){x={tmp:''}}
//set some placeholding values to avoid "undefined" in ffmpeg string.
x.record_string=''
x.cust_input=''
x.cust_detect=' '
x.record_video_filters=[]
x.stream_video_filters=[]
//input - analyze duration
if(e.details.aduration&&e.details.aduration!==''){x.cust_input+=' -analyzeduration '+e.details.aduration};
//input - probe size
if(e.details.probesize&&e.details.probesize!==''){x.cust_input+=' -probesize '+e.details.probesize};
//input - check protocol
//input
switch(e.type){
case'h264':
switch(e.protocol){
case'rtsp':
if(e.details.rtsp_transport&&e.details.rtsp_transport!==''&&e.details.rtsp_transport!=='no'){x.cust_input+=' -rtsp_transport '+e.details.rtsp_transport;}
break;
}
break;
}
//record - resolution
switch(s.ratio(e.width,e.height)){
case'16:9':
x.ratio='640x360';
break;
default:
x.ratio='640x480';
break;
}
if(e.width!==''&&e.height!==''&&!isNaN(e.width)&&!isNaN(e.height)){
x.record_dimensions=' -s '+e.width+'x'+e.height
}else{
x.record_dimensions=''
}
if(e.details.stream_scale_x&&e.details.stream_scale_x!==''&&e.details.stream_scale_y&&e.details.stream_scale_y!==''){
x.ratio=e.details.stream_scale_x+'x'+e.details.stream_scale_y;
}
//record - segmenting
x.segment=' -f segment -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' ';
//record - check for double quotes
if(e.details.dqf=='1'){
x.segment+='"'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
}else{
x.segment+=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}
break;
case'webm':
x.acodec='libvorbis',x.vcodec='libvpx';
if(e.details.crf&&e.details.crf!==''){x.vcodec+=' -q:v '+e.details.crf}else{x.vcodec+=' -q:v 1';}
break;
}
//record - use custom video codec
if(e.details.vcodec&&e.details.vcodec!==''&&e.details.vcodec!=='default'){x.vcodec=e.details.vcodec}
//record - use custom audio codec
if(e.details.acodec&&e.details.acodec!==''&&e.details.acodec!=='default'){x.acodec=e.details.acodec}
if(e.details.cust_record){
if(x.acodec=='aac'&&e.details.cust_record.indexOf('-strict -2')===-1){e.details.cust_record+=' -strict -2';}
if(e.details.cust_record.indexOf('-threads')===-1){e.details.cust_record+=' -threads 1';}
}
// if(e.details.cust_input&&(e.details.cust_input.indexOf('-use_wallclock_as_timestamps 1')>-1)===false){e.details.cust_input+=' -use_wallclock_as_timestamps 1';}
//record - ready or reset codecs
if(x.acodec!=='no'){
if(x.acodec.indexOf('none')>-1){x.acodec=''}else{x.acodec=' -acodec '+x.acodec}
}else{
x.acodec=' -an'
}
if(x.vcodec.indexOf('none')>-1){x.vcodec=''}else{x.vcodec=' -vcodec '+x.vcodec}
//stream - frames per second
if(!e.details.sfps||e.details.sfps===''){
e.details.sfps=parseFloat(e.details.sfps);
if(isNaN(e.details.sfps)){e.details.sfps=1}
}
if(e.fps&&e.fps!==''){x.framerate=' -r '+e.fps}else{x.framerate=''}
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
if(e.details.timestamp_font&&e.details.timestamp_font!==''){x.time_font=e.details.timestamp_font}else{x.time_font='/usr/share/fonts/truetype/freefont/FreeSans.ttf'}
//position x
if(e.details.timestamp_x&&e.details.timestamp_x!==''){x.timex=e.details.timestamp_x}else{x.timex='(w-tw)/2'}
//position y
if(e.details.timestamp_y&&e.details.timestamp_y!==''){x.timey=e.details.timestamp_y}else{x.timey='0'}
//text color
if(e.details.timestamp_color&&e.details.timestamp_color!==''){x.time_color=e.details.timestamp_color}else{x.time_color='white'}
//box color
if(e.details.timestamp_box_color&&e.details.timestamp_box_color!==''){x.time_box_color=e.details.timestamp_box_color}else{x.time_box_color='0x00000000@1'}
//text size
if(e.details.timestamp_font_size&&e.details.timestamp_font_size!==''){x.time_font_size=e.details.timestamp_font_size}else{x.time_font_size='10'}
x.record_video_filters.push('drawtext=fontfile='+x.time_font+':text=\'%{localtime}\':x='+x.timex+':y='+x.timey+':fontcolor='+x.time_color+':box=1:boxcolor='+x.time_box_color+':fontsize='+x.time_font_size);
}
//record - watermark for -vf
if(e.details.watermark&&e.details.watermark=="1"&&e.details.watermark_location&&e.details.watermark_location!==''){
switch(e.details.watermark_position){
case'tl'://top left
x.watermark_position='10:10'
break;
case'tr'://top right
x.watermark_position='main_w-overlay_w-10:10'
break;
case'bl'://bottom left
x.watermark_position='10:main_h-overlay_h-10'
break;
default://bottom right
x.watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2'
break;
}
x.record_video_filters.push('movie='+e.details.watermark_location+'[watermark],[in][watermark]overlay='+x.watermark_position+'[out]');
}
//record - rotation
if(e.details.rotate_record&&e.details.rotate_record!==""&&e.details.rotate_record!=="no"){
x.record_video_filters.push('transpose='+e.details.rotate_record);
}
//check custom record filters for -vf
if(e.details.vf&&e.details.vf!==''){
x.record_video_filters.push(e.details.vf)
}
//compile filter string for -vf
if(x.record_video_filters.length>0){
x.record_video_filters=' -vf '+x.record_video_filters.join(',')
}else{
x.record_video_filters=''
}
//stream - timestamp
if(e.details.stream_timestamp&&e.details.stream_timestamp=="1"&&e.details.vcodec!=='copy'){
//font
if(e.details.stream_timestamp_font&&e.details.stream_timestamp_font!==''){x.stream_timestamp_font=e.details.stream_timestamp_font}else{x.stream_timestamp_font='/usr/share/fonts/truetype/freefont/FreeSans.ttf'}
//position x
if(e.details.stream_timestamp_x&&e.details.stream_timestamp_x!==''){x.stream_timestamp_x=e.details.stream_timestamp_x}else{x.stream_timestamp_x='(w-tw)/2'}
//position y
if(e.details.stream_timestamp_y&&e.details.stream_timestamp_y!==''){x.stream_timestamp_y=e.details.stream_timestamp_y}else{x.stream_timestamp_y='0'}
//text color
if(e.details.stream_timestamp_color&&e.details.stream_timestamp_color!==''){x.stream_timestamp_color=e.details.stream_timestamp_color}else{x.stream_timestamp_color='white'}
//box color
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);
}
//stream - watermark for -vf
if(e.details.stream_watermark&&e.details.stream_watermark=="1"&&e.details.stream_watermark_location&&e.details.stream_watermark_location!==''){
switch(e.details.stream_watermark_position){
case'tl'://top left
x.stream_watermark_position='10:10'
break;
case'tr'://top right
x.stream_watermark_position='main_w-overlay_w-10:10'
break;
case'bl'://bottom left
x.stream_watermark_position='10:main_h-overlay_h-10'
break;
default://bottom right
x.stream_watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2'
break;
}
x.stream_video_filters.push('movie='+e.details.stream_watermark_location+'[watermark],[in][watermark]overlay='+x.stream_watermark_position+'[out]');
}
//stream - rotation
if(e.details.rotate_stream&&e.details.rotate_stream!==""&&e.details.rotate_stream!=="no"){
x.stream_video_filters.push('transpose='+e.details.rotate_stream);
}
//stream - video filter
if(e.details.svf&&e.details.svf!==''){
x.stream_video_filters.push(e.details.svf)
}
if(x.stream_video_filters.length>0){
x.stream_video_filters=' -vf '+x.stream_video_filters.join(',')
}else{
x.stream_video_filters=''
}
//stream - hls vcodec
if(e.details.stream_vcodec&&e.details.stream_vcodec!=='no'){
if(e.details.stream_vcodec!==''){x.stream_vcodec=' -c:v '+e.details.stream_vcodec}else{x.stream_vcodec='libx264'}
}else{
x.stream_vcodec='';
}
//stream - hls acodec
if(e.details.stream_acodec!=='no'){
if(e.details.stream_acodec&&e.details.stream_acodec!==''){x.stream_acodec=' -c:a '+e.details.stream_acodec}else{x.stream_acodec=''}
}else{
x.stream_acodec=' -an';
}
//stream - hls segment time
if(e.details.hls_time&&e.details.hls_time!==''){x.hls_time=e.details.hls_time}else{x.hls_time=2} //hls list size
if(e.details.hls_list_size&&e.details.hls_list_size!==''){x.hls_list_size=e.details.hls_list_size}else{x.hls_list_size=2}
//stream - custom flags
if(e.details.cust_stream&&e.details.cust_stream!==''){x.cust_stream=' '+e.details.cust_stream}else{x.cust_stream=''}
//stream - preset
if(e.details.preset_stream&&e.details.preset_stream!==''){x.preset_stream=' -preset '+e.details.preset_stream;}else{x.preset_stream=''}
//stream - quality
if(e.details.stream_quality&&e.details.stream_quality!==''){x.stream_quality=e.details.stream_quality}else{x.stream_quality=''}
//stream - pipe build
switch(e.details.stream_type){
case'hls':
if(x.cust_stream.indexOf('-tune')===-1){x.cust_stream+=' -tune zerolatency'}
if(x.cust_stream.indexOf('-g ')===-1){x.cust_stream+=' -g 1'}
if(x.stream_quality)x.stream_quality=' -crf '+x.stream_quality;
x.pipe=x.preset_stream+x.stream_quality+x.stream_acodec+x.stream_vcodec+x.stream_fps+' -f hls -s '+x.ratio+x.stream_video_filters+x.cust_stream+' -hls_time '+x.hls_time+' -hls_list_size '+x.hls_list_size+' -start_number 0 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist '+e.sdir+'s.m3u8';
break;
case'mjpeg':
if(x.stream_quality)x.stream_quality=' -q:v '+x.stream_quality;
x.pipe=' -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+x.stream_quality+x.stream_fps+' -s '+x.ratio+' pipe:1';
break;
case'b64':case'':case undefined:case null://base64
if(x.stream_quality)x.stream_quality=' -q:v '+x.stream_quality;
x.pipe=' -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+x.stream_quality+x.stream_fps+' -s '+x.ratio+' pipe:1';
break;
default:
x.pipe=''
break;
}
//detector - plugins, motion
if(e.details.detector==='1'&&e.details.detector_send_frames==='1'){
if(!e.details.detector_fps||e.details.detector_fps===''){e.details.detector_fps=2}
if(e.details.detector_scale_x&&e.details.detector_scale_x!==''&&e.details.detector_scale_y&&e.details.detector_scale_y!==''){x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y}else{x.dratio=' -s 320x240'}
if(e.details.cust_detect&&e.details.cust_detect!==''){x.cust_detect+=e.details.cust_detect;}
x.pipe+=' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:0';
}
//api - snapshot bin/ cgi.bin (JPEG Mode)
if(e.details.snap==='1'||e.details.stream_type==='jpeg'){
if(!e.details.snap_fps||e.details.snap_fps===''){e.details.snap_fps=1}
if(e.details.snap_vf&&e.details.snap_vf!==''){x.snap_vf=' -vf '+e.details.snap_vf}else{x.snap_vf=''}
if(e.details.snap_scale_x&&e.details.snap_scale_x!==''&&e.details.snap_scale_y&&e.details.snap_scale_y!==''){x.sratio=' -s '+e.details.snap_scale_x+'x'+e.details.snap_scale_y}else{x.sratio=''}
if(e.details.cust_snap&&e.details.cust_snap!==''){x.cust_snap=' '+e.details.cust_snap;}else{x.cust_snap=''}
x.pipe+=' -update 1 -r '+e.details.snap_fps+x.cust_snap+x.sratio+x.snap_vf+' '+e.sdir+'s.jpg -y';
}
// //Stream to YouTube (Stream out to server)
// if(e.details.stream_server==='1'){
// if(!e.details.stream_server_vbr||e.details.stream_server_vbr===''){e.details.stream_server_vbr='256k'}
// x.stream_server_vbr=' -b:v '+e.details.stream_server_vbr;
// if(e.details.stream_server_fps&&e.details.stream_server_fps!==''){
// x.stream_server_fps=' -r '+e.details.stream_server_fps
// e.details.stream_server_fps=parseFloat(e.details.stream_server_fps)
// x.stream_server_fps+=' -g '+e.details.stream_server_fps
// }else{x.stream_server_fps=''}
// if(e.details.stream_server_crf&&e.details.stream_server_crf!==''){x.stream_server_crf=' -crf '+e.details.stream_server_crf}else{x.stream_server_crf=''}
// if(e.details.stream_server_vf&&e.details.stream_server_vf!==''){x.stream_server_vf=' -vf '+e.details.stream_server_vf}else{x.stream_server_vf=''}
// if(e.details.stream_server_preset&&e.details.stream_server_preset!==''){x.stream_server_preset=' -preset '+e.details.stream_server_preset}else{x.stream_server_preset=''}
// if(e.details.stream_server_scale_x&&e.details.stream_server_scale_x!==''&&e.details.stream_server_scale_y&&e.details.stream_server_scale_y!==''){x.stream_server_ratio=' -s '+e.details.stream_server_scale_x+'x'+e.details.stream_server_scale_y}else{x.stream_server_ratio=''}
// if(e.details.cust_stream_server&&e.details.cust_stream_server!==''){x.cust_stream_server=' '+e.details.cust_stream_server;}else{x.cust_stream_server=''}
// x.pipe+=' -vcodec libx264 -pix_fmt yuv420p'+x.stream_server_preset+x.stream_server_crf+x.stream_server_fps+x.stream_server_vbr+x.stream_server_ratio+x.stream_server_vf+' -acodec aac -strict 2 -ar 44100 -q:a 3 -b:a 712000'+x.cust_stream_server+' -f flv '+e.details.stream_server_url;
// }
//custom - output
if(e.details.custom_output&&e.details.custom_output!==''){x.pipe+=' '+e.details.custom_output;}
//custom - input flags
if(e.details.cust_input&&e.details.cust_input!==''){x.cust_input+=' '+e.details.cust_input;}
//logging - level
if(e.details.loglevel&&e.details.loglevel!==''){x.loglevel='-loglevel '+e.details.loglevel;}else{x.loglevel='-loglevel error'}
if(e.mode=='record'){
//custom - record flags
if(e.details.cust_record&&e.details.cust_record!==''){x.record_string+=' '+e.details.cust_record;}
//record - preset
if(e.details.preset_record&&e.details.preset_record!==''){x.record_string+=' -preset '+e.details.preset_record;}
}
//build final string based on the input type.
switch(e.type){
case'dashcam':
if(e.mode==='record'){x.record_string+=x.vcodec+x.framerate+x.record_video_filters+x.record_dimensions+x.segment;}
x.tmp=x.loglevel+' -i -'+x.record_string+x.pipe;
break;
case'socket':case'jpeg':case'pipe':
if(e.mode==='record'){x.record_string+=x.vcodec+x.framerate+x.record_video_filters+x.record_dimensions+x.segment;}
x.tmp=x.loglevel+' -pattern_type glob -f image2pipe'+x.framerate+' -vcodec mjpeg'+x.cust_input+' -i -'+x.record_string+x.pipe;
break;
case'mjpeg':
if(e.mode=='record'){
x.record_string+=x.vcodec+x.record_video_filters+x.framerate+x.record_dimensions+x.segment;
}
x.tmp=x.loglevel+' -reconnect 1 -r '+e.details.sfps+' -f mjpeg'+x.cust_input+' -i '+e.url+''+x.record_string+x.pipe;
break;
case'h264':case'hls':case'mp4':
if(e.mode=='record'){
x.record_string+=x.vcodec+x.framerate+x.acodec+x.record_dimensions+x.record_video_filters+' '+x.segment;
}
x.tmp=x.loglevel+x.cust_input+' -i '+e.url+x.record_string+x.pipe;
break;
case'local':
if(e.mode=='record'){
x.record_string+=x.vcodec+x.framerate+x.acodec+x.record_dimensions+x.record_video_filters+' '+x.segment;
}
x.tmp=x.loglevel+x.cust_input+' -i '+e.path+''+x.record_string+x.pipe;
break;
}
s.group[e.ke].mon[e.mid].ffmpeg=x.tmp;
return spawn(config.ffmpegDir,x.tmp.replace(/\s+/g,' ').trim().split(' '),{detached: true});
}
s.file=function(x,e){
if(!e){e={}};
switch(x){
case'size':
return fs.statSync(e.filename)["size"];
break;
case'delete':
if(!e){return false;}
return exec('rm -rf '+e,{detached: true});
break;
case'delete_files':
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 -rf {} +',{detached: true});
break;
}
}
s.camera=function(x,e,cn,tx){
if(x!=='motion'){
var ee=s.init('noReference',e);
if(!e){e={}};if(cn&&cn.ke&&!e.ke){e.ke=cn.ke};
if(!e.mode){e.mode=x;}
if(!e.id&&e.mid){e.id=e.mid}
}
if(e.details&&(e.details instanceof Object)===false){
try{e.details=JSON.parse(e.details)}catch(err){}
}
['detector_cascades','cords'].forEach(function(v){
if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){
try{
e.details[v]=JSON.parse(e.details[v]);
if(!e.details[v])e.details[v]={};
}catch(err){
e.details[v]={};
}
}
})
switch(x){
case'snapshot'://get snapshot from monitor URL
if(config.doSnapshot===true){
if(e.mon.mode!=='stop'){
try{e.mon.details=JSON.parse(e.mon.details)}catch(er){}
if(e.mon.details.snap==='1'){
fs.readFile(s.dir.streams+e.ke+'/'+e.mid+'/s.jpg',function(err,data){
if(err){s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke);return};
s.tx({f:'monitor_snapshot',snapshot:data,snapshot_format:'ab',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
})
}else{
e.url=s.init('url',e.mon);
switch(e.mon.type){
case'mjpeg':case'h264':case'local':
if(e.mon.type==='local'){e.url=e.mon.path;}
e.spawn=spawn(config.ffmpegDir,('-loglevel quiet -i '+e.url+' -s 400x400 -r 25 -ss 1.8 -frames:v 1 -f singlejpeg pipe:1').split(' '),{detached: true})
e.spawn.stdout.on('data',function(data){
e.snapshot_sent=true; s.tx({f:'monitor_snapshot',snapshot:data.toString('base64'),snapshot_format:'b64',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
e.spawn.kill();
});
e.spawn.on('close',function(data){
if(!e.snapshot_sent){
s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
}
delete(e.snapshot_sent);
});
break;
case'jpeg':
request({url:e.url,method:'GET',encoding:null},function(err,data){
if(err){s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke);return};
s.tx({f:'monitor_snapshot',snapshot:data.body,snapshot_format:'ab',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
})
break;
default:
s.tx({f:'monitor_snapshot',snapshot:'...',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
break;
}
}
}else{
s.tx({f:'monitor_snapshot',snapshot:'Disabled',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
}
}else{
s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
}
break;
case'recor