Merge branch 'dev' into 'master'

Shiny Pikachu

See merge request Shinobi-Systems/Shinobi!14
merge-requests/23/head
Moe 2018-07-11 02:38:47 +00:00
commit 7c35dfa6f6
14 changed files with 785 additions and 261 deletions

View File

@ -102,6 +102,7 @@ Courthouse Vancouver Robson Square
Node.js - https://nodejs.org/en/
MariaDB - https://mariadb.org/
FFmpeg - https://www.ffmpeg.org/
request - https://www.npmjs.com/package/request
Express (npm) - https://expressjs.com/ https://www.npmjs.com/package/express
EJS (npm) - http://ejs.co/ https://www.npmjs.com/package/ejs
pam-diff (npm) (Motion Detector) - https://github.com/kevinGodell/pam-diff

587
camera.js
View File

@ -116,7 +116,6 @@ if(config.databaseType===undefined){config.databaseType='mysql'}
if(config.pluginKeys===undefined)config.pluginKeys={};
if(config.databaseLogs===undefined){config.databaseLogs=false}
if(config.useUTC===undefined){config.useUTC=false}
if(config.strictDatabase===undefined){config.strictDatabase=false}
if(config.pipeAddition===undefined){config.pipeAddition=7}else{config.pipeAddition=parseInt(config.pipeAddition)}
//Web Paths
if(config.webPaths===undefined){config.webPaths={}}
@ -220,42 +219,24 @@ if(databaseOptions.client === 'sqlite3' && databaseOptions.connection.filename =
databaseOptions.connection.filename = __dirname+"/shinobi.sqlite"
}
s.databaseEngine = knex(databaseOptions)
s.sqlDate = function(value){
var dateQueryFunction = ''
if(databaseOptions.client === 'sqlite3'){
value = value.toLowerCase()
if (value.slice(-1) !== 's') {
value = value+'s'
}
dateQueryFunction = "datetime('now', '-"+value+"')"
}else{
value = value.toUpperCase()
if (value.slice(-1) === 'S') {
value = value.slice(0, -1);
}
dateQueryFunction = "DATE_SUB(NOW(), INTERVAL "+value+")"
}
return dateQueryFunction
}
s.mergeQueryValues = function(query,values){
if(!values){values=[]}
var valuesNotFunction = true;
if(typeof values === 'function'){
var onMoveOn = values;
var values = [];
valuesNotFunction = false;
}
if(!onMoveOn){onMoveOn=function(){}}
if(values&&valuesNotFunction){
var splitQuery = query.split('?')
var newQuery = ''
splitQuery.forEach(function(v,n){
newQuery += v
if(values[n]){
if(isNaN(values[n])){
newQuery += "'"+values[n]+"'"
var value = values[n]
if(value){
if(isNaN(value) || value instanceof Date){
newQuery += "'"+value+"'"
}else{
newQuery += values[n]
newQuery += value
}
}
})
@ -264,7 +245,11 @@ s.mergeQueryValues = function(query,values){
}
return newQuery
}
s.sqlQuery = function(query,values,onMoveOn,hideLog){
s.stringToSqlTime = function(value){
newValue = new Date(value.replace('T',' '))
return newValue
}
s.sqlQuery = function(query,values,onMoveOn){
if(!values){values=[]}
if(typeof values === 'function'){
var onMoveOn = values;
@ -272,24 +257,53 @@ s.sqlQuery = function(query,values,onMoveOn,hideLog){
}
if(!onMoveOn){onMoveOn=function(){}}
var mergedQuery = s.mergeQueryValues(query,values)
return s.databaseEngine.raw(query,values)
.asCallback(function(err,r){
if(err&&config.databaseLogs){
s.systemLog('s.sqlQuery QUERY',query)
s.systemLog('s.sqlQuery ERROR',err)
s.debugLog('s.sqlQuery QUERY',mergedQuery)
return s.databaseEngine
.raw(query,values)
.asCallback(function(err,r){
if(err){
console.log('s.sqlQuery QUERY ERRORED',query)
console.log('s.sqlQuery ERROR',err)
}
if(onMoveOn && typeof onMoveOn === 'function'){
switch(databaseOptions.client){
case'sqlite3':
if(!r)r=[]
break;
default:
if(r)r=r[0]
break;
}
if(onMoveOn && typeof onMoveOn === 'function'){
switch(databaseOptions.client){
case'sqlite3':
if(!r)r=[]
break;
default:
if(r)r=r[0]
break;
onMoveOn(err,r)
}
})
}
//discord bot
if(config.discordBot === true){
try{
var Discord = require("discord.js")
s.sendDiscordAlert = function(data,files,groupKey){
if(!data)data = {};
var sendBody = Object.assign({
color: 3447003,
title: 'Alert from Shinobi',
description: "",
fields: [],
timestamp: new Date(),
footer: {
icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png",
text: "Shinobi Systems"
}
onMoveOn(err,r)
}
})
},data)
s.group[groupKey].discordBot.channels.get(s.group[groupKey].init.discordbot_channel).send({
embed: sendBody,
files: files
})
}
}catch(err){
console.log('Could not start Discord bot, please run "npm install discord.js" inside the Shinobi folder.')
s.sendDiscordAlert = function(){}
}
}
//kill any ffmpeg running
s.ffmpegKill=function(){
@ -345,7 +359,7 @@ s.txWithSubPermissions = function(z,y,permissionChoices){
var valid=0
var checked=permissionChoices.length
permissionChoices.forEach(function(b){
if(user.details[b].indexOf(z.mid)!==-1){
if(user.details[b] && user.details[b].indexOf(z.mid)!==-1){
++valid
}
})
@ -449,6 +463,21 @@ s.getFunctionParamNames = function(func) {
result = [];
return result;
}
s.getDetectorStreams = function(monitor){
var pathDir = s.dir.streams+monitor.ke+'/'+monitor.id+'/'
var streamDirItems = fs.readdirSync(pathDir)
var items = []
streamDirItems.forEach(function(filename){
if(filename.indexOf('detectorStream') > -1 && filename.indexOf('.m3u8') === -1){
try{
items.push(pathDir+filename)
}catch(err){
console.log(err)
}
}
})
return items
}
s.createPamDiffRegionArray = function(regions,globalSensitivity,fullFrame){
var pamDiffCompliantArray = [],
arrayForOtherStuff = [],
@ -494,7 +523,7 @@ s.getRequest = function(url,callback){
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){
s.group[e.ke].mon[e.mid].allowStdinWrite = false
s.group[e.ke].mon[e.id].allowStdinWrite = false
s.txToDashcamUsers({
f : 'disable_stream',
ke : e.ke,
@ -549,7 +578,7 @@ s.log=function(e,x){
// s.systemLog('s.log : ',{f:'log',ke:e.ke,mid:e.mid,log:x,time:s.timeObject()},'GRP_'+e.ke)
}
//system log
s.systemLog=function(q,w,e){
s.systemLog = function(q,w,e){
if(!w){w=''}
if(!e){e=''}
if(config.systemLog===true){
@ -560,6 +589,17 @@ s.systemLog=function(q,w,e){
return console.log(s.timeObject().format(),q,w,e)
}
}
//system log
s.debugLog = function(q,w,e){
if(!w){w = ''}
if(!e){e = ''}
if(config.debugLog === true){
console.log(s.timeObject().format(),q,w,e)
if(config.debugLogVerbose === true){
console.log(new Error())
}
}
}
//SSL options
if(config.ssl&&config.ssl.key&&config.ssl.cert){
config.ssl.key=fs.readFileSync(s.checkRelativePath(config.ssl.key),'utf8')
@ -659,12 +699,7 @@ s.init=function(x,e,k,fn){
switch(x){
case 0://init camera
if(!s.group[e.ke]){s.group[e.ke]={}};
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
if(!s.group[e.ke].mon){s.group[e.ke].mon={}}
if(!s.group[e.ke].sizeChangeQueue){s.group[e.ke].sizeChangeQueue=[]}
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
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].mon[e.mid]){s.group[e.ke].mon[e.mid]={}}
if(!s.group[e.ke].mon[e.mid].streamIn){s.group[e.ke].mon[e.mid].streamIn={}};
if(!s.group[e.ke].mon[e.mid].emitterChannel){s.group[e.ke].mon[e.mid].emitterChannel={}};
@ -679,7 +714,6 @@ s.init=function(x,e,k,fn){
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'group':
if(!s.group[e.ke]){
@ -688,6 +722,11 @@ s.init=function(x,e,k,fn){
if(!s.group[e.ke].init){
s.group[e.ke].init={}
}
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
if(!s.group[e.ke].sizeChangeQueue){s.group[e.ke].sizeChangeQueue=[]}
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
if(!s.group[e.ke].users){s.group[e.ke].users={}}
if(!s.group[e.ke].dashcamUsers){s.group[e.ke].dashcamUsers={}}
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;
@ -723,6 +762,18 @@ s.init=function(x,e,k,fn){
ar.webdav_pass
);
}
//discordbot
if(!s.group[e.ke].discordBot &&
config.discordBot === true &&
ar.discordbot === '1' &&
ar.discordbot_token !== ''
){
s.group[e.ke].discordBot = new Discord.Client()
s.group[e.ke].discordBot.on('ready', () => {
console.log(`${r.mail} : Discord Bot Logged in as ${s.group[e.ke].discordBot.user.tag}!`)
})
s.group[e.ke].discordBot.login(ar.discordbot_token)
}
Object.keys(ar).forEach(function(v){
s.group[e.ke].init[v]=ar[v]
})
@ -918,15 +969,12 @@ s.video=function(x,e,k){
time = e.time
}
time = new Date(time)
if(config.databaseType !== 'sqlite'){
time = moment(time).format('YYYY-MM-DD HH:mm:ss')
}
e.save=[e.id,e.ke,time];
s.sqlQuery('SELECT * FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=? LIMIT 1',e.save,function(err,r){
s.sqlQuery('SELECT * FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',e.save,function(err,r){
if(r&&r[0]){
r=r[0]
var dir=s.video('getDir',r)
s.sqlQuery('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=? LIMIT 1',e.save,function(){
s.sqlQuery('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',e.save,function(){
fs.stat(dir+filename,function(err,file){
if(err){
s.systemLog('File Delete Error : '+e.ke+' : '+' : '+e.mid,err)
@ -1093,7 +1141,7 @@ s.video=function(x,e,k){
if(!evs)return console.log(err)
evs.forEach(function(ev){
ev.dir=s.video('getDir',ev)+s.formattedTime(ev.time)+'.'+ev.ext;
k.del.push('(mid=? AND time=?)');
k.del.push('(mid=? AND `time`=?)');
k.ar.push(ev.mid),k.ar.push(ev.time);
s.file('delete',ev.dir);
s.init('diskUsedSet',e,-(ev.size/1000000))
@ -1238,10 +1286,6 @@ s.video=function(x,e,k){
k.details.dir = e.details.dir
}
if(config.useUTC === true)k.details.isUTC = config.useUTC;
if(config.strictDatabase === true){
e.startTime = s.formattedTime(e.startTime)
e.endTime = s.formattedTime(e.endTime)
}
var save = [
e.mid,
e.ke,
@ -2442,9 +2486,7 @@ s.camera=function(x,e,cn,tx){
e.details.fatal_max = parseFloat(e.details.fatal_max)
}
var errorFatal = function(errorMessage){
if(config.debugSystem === true){
console.log(errorMessage,(new Error()).stack)
}
s.debugLog(errorMessage)
clearTimeout(s.group[e.ke].mon[e.id].err_fatal_timeout);
++errorFatalCount;
if(s.group[e.ke].mon[e.id].started===1){
@ -2518,7 +2560,7 @@ s.camera=function(x,e,cn,tx){
cutoff *= 100
}
s.group[e.ke].mon[e.id].checker=setTimeout(function(){
if(s.group[e.ke].mon[e.id].started === 1){
if(s.group[e.ke].mon[e.id].started === 1 && s.group[e.ke].mon_conf[e.id].mode === 'record'){
launchMonitorProcesses();
s.init('monitorStatus',{id:e.id,ke:e.ke,status:lang.Restarting});
s.log(e,{type:lang['Camera is not recording'],msg:{msg:lang['Restarting Process']}});
@ -3193,8 +3235,71 @@ s.camera=function(x,e,cn,tx){
}).end();
}
var screenshotName = 'Motion_'+(d.mon.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()
var screenshotBuffer = null
var detectorStreamBuffers = null
//discord bot
if(d.mon.details.detector_discordbot === '1' && !s.group[d.ke].mon[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;
}else{
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(){
//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);
var files = []
var sendAlert = function(){
s.sendDiscordAlert({
author: {
name: s.group[d.ke].mon_conf[d.id].name,
icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png"
},
title: lang.Event+' - '+screenshotName,
description: lang.EventText1+' '+s.timeObject(new Date).format(),
fields: [],
timestamp: new Date(),
footer: {
icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png",
text: "Shinobi Systems"
}
},files,d.ke)
}
if(!detectorStreamBuffers){
detectorStreamBuffers = s.getDetectorStreams(d)
}
detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){
files.push({
attachment: filepath,
name: 'Video Clip '+n+'.ts'
})
})
if(screenshotBuffer){
sendAlert()
}else if(d.mon.details.snap === '1'){
fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){
if(err){
s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err)
}else{
screenshotBuffer = frame
files.push({
attachment: screenshotBuffer,
name: screenshotName+'.jpg'
})
}
sendAlert()
})
}else{
sendAlert()
}
}
//mailer
if(config.mail&&!s.group[d.ke].mon[d.id].detector_mail&&d.mon.details.detector_mail==='1'){
if(config.mail && !s.group[d.ke].mon[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
@ -3209,35 +3314,53 @@ s.camera=function(x,e,cn,tx){
clearTimeout(s.group[d.ke].mon[d.id].detector_mail);
delete(s.group[d.ke].mon[d.id].detector_mail);
},detector_mail_timeout);
d.frame_filename='Motion_'+(d.mon.name.replace(/[^\w\s]/gi, ''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()+'.jpg';
fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){
var files = []
var sendMail = function(){
d.mailOptions = {
from: '"ShinobiCCTV" <no-reply@shinobi.video>', // sender address
to: r.mail, // list of receivers
subject: lang.Event+' - '+d.frame_filename, // Subject line
subject: lang.Event+' - '+screenshotName, // Subject line
html: '<i>'+lang.EventText1+' '+s.timeObject(new Date).format()+'.</i>',
};
if(err){
s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err)
}else{
d.mailOptions.attachments=[
{
filename: d.frame_filename,
content: frame
}
]
d.mailOptions.html='<i>'+lang.EventText3+'</i>'
attachments: files
}
Object.keys(d.details).forEach(function(v,n){
Object.keys(d.details).forEach(function(v,n){
d.mailOptions.html+='<div><b>'+v+'</b> : '+d.details[v]+'</div>'
})
nodemailer.sendMail(d.mailOptions, (error, info) => {
if (error) {
s.systemLog(lang.MailError,error)
return ;
return false;
}
});
})
}
if(!detectorStreamBuffers){
detectorStreamBuffers = s.getDetectorStreams(d)
}
detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){
files.push({
attachment: filepath,
name: 'Video Clip '+n+'.ts'
})
})
if(screenshotBuffer){
sendMail()
}else if(d.mon.details.snap === '1'){
fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){
if(err){
s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err)
}else{
screenshotBuffer = frame
files.push({
filename: screenshotName+'.jpg',
content: frame
})
d.mailOptions.html='<i>'+lang.EventText3+'</i>'
}
sendMail()
})
}else{
sendMail()
}
});
}
if(d.mon.details.detector_command_enable==='1'&&!s.group[d.ke].mon[d.id].detector_command){
@ -3839,57 +3962,64 @@ var tx;
if(r&&r[0]){
r=r[0];
d.d=JSON.parse(r.details);
if(d.d.get_server_log==='1'){
cn.join('GRPLOG_'+d.ke)
}else{
cn.leave('GRPLOG_'+d.ke)
}
///unchangeable from client side, so reset them in case they did.
d.form.details=JSON.parse(d.form.details)
//admin permissions
d.form.details.permissions=d.d.permissions
d.form.details.edit_size=d.d.edit_size
d.form.details.edit_days=d.d.edit_days
d.form.details.use_admin=d.d.use_admin
d.form.details.use_webdav=d.d.use_webdav
d.form.details.use_ldap=d.d.use_ldap
//check
if(d.d.edit_days=="0"){
d.form.details.days=d.d.days;
}
if(d.d.edit_size=="0"){
d.form.details.size=d.d.size;
}
if(d.d.sub){
d.form.details.sub=d.d.sub;
if(d.d.monitors){d.form.details.monitors=d.d.monitors;}
if(d.d.allmonitors){d.form.details.allmonitors=d.d.allmonitors;}
if(d.d.video_delete){d.form.details.video_delete=d.d.video_delete;}
if(d.d.video_view){d.form.details.video_view=d.d.video_view;}
if(d.d.monitor_edit){d.form.details.monitor_edit=d.d.monitor_edit;}
if(d.d.size){d.form.details.size=d.d.size;}
if(d.d.days){d.form.details.days=d.d.days;}
delete(d.form.details.mon_groups)
}
var newSize = d.form.details.size
d.form.details=JSON.stringify(d.form.details)
///
d.set=[],d.ar=[];
if(d.form.pass&&d.form.pass!==''){d.form.pass=s.md5(d.form.pass);}else{delete(d.form.pass)};
delete(d.form.password_again);
d.for=Object.keys(d.form);
d.for.forEach(function(v){
d.set.push(v+'=?'),d.ar.push(d.form[v]);
});
d.ar.push(d.ke),d.ar.push(d.uid);
s.sqlQuery('UPDATE Users SET '+d.set.join(',')+' WHERE ke=? AND uid=?',d.ar,function(err,r){
if(!d.d.sub){
s.group[d.ke].sizeLimit = parseFloat(newSize)
delete(s.group[d.ke].webdav)
s.init('apps',d)
if(!d.d.sub || d.d.user_change === "1"){
if(d.d.get_server_log==='1'){
cn.join('GRPLOG_'+d.ke)
}else{
cn.leave('GRPLOG_'+d.ke)
}
tx({f:'user_settings_change',uid:d.uid,ke:d.ke,form:d.form});
});
///unchangeable from client side, so reset them in case they did.
d.form.details=JSON.parse(d.form.details)
//admin permissions
d.form.details.permissions=d.d.permissions
d.form.details.edit_size=d.d.edit_size
d.form.details.edit_days=d.d.edit_days
d.form.details.use_admin=d.d.use_admin
d.form.details.use_webdav=d.d.use_webdav
d.form.details.use_ldap=d.d.use_ldap
//check
if(d.d.edit_days=="0"){
d.form.details.days=d.d.days;
}
if(d.d.edit_size=="0"){
d.form.details.size=d.d.size;
}
if(d.d.sub){
d.form.details.sub=d.d.sub;
if(d.d.monitors){d.form.details.monitors=d.d.monitors;}
if(d.d.allmonitors){d.form.details.allmonitors=d.d.allmonitors;}
if(d.d.monitor_create){d.form.details.monitor_create=d.d.monitor_create;}
if(d.d.video_delete){d.form.details.video_delete=d.d.video_delete;}
if(d.d.video_view){d.form.details.video_view=d.d.video_view;}
if(d.d.monitor_edit){d.form.details.monitor_edit=d.d.monitor_edit;}
if(d.d.size){d.form.details.size=d.d.size;}
if(d.d.days){d.form.details.days=d.d.days;}
delete(d.form.details.mon_groups)
}
var newSize = d.form.details.size
d.form.details=JSON.stringify(d.form.details)
///
d.set=[],d.ar=[];
if(d.form.pass&&d.form.pass!==''){d.form.pass=s.md5(d.form.pass);}else{delete(d.form.pass)};
delete(d.form.password_again);
d.for=Object.keys(d.form);
d.for.forEach(function(v){
d.set.push(v+'=?'),d.ar.push(d.form[v]);
});
d.ar.push(d.ke),d.ar.push(d.uid);
s.sqlQuery('UPDATE Users SET '+d.set.join(',')+' WHERE ke=? AND uid=?',d.ar,function(err,r){
if(!d.d.sub){
s.group[d.ke].sizeLimit = parseFloat(newSize)
delete(s.group[d.ke].webdav)
if(s.group[d.ke].discordBot && s.group[d.ke].discordBot.destroy){
s.group[d.ke].discordBot.destroy()
delete(s.group[d.ke].discordBot)
}
s.init('apps',d)
}
tx({f:'user_settings_change',uid:d.uid,ke:d.ke,form:d.form});
});
}
}
})
break;
@ -3901,15 +4031,15 @@ var tx;
switch(d.fff){
case'videos&events':
if(!d.eventLimit){
d.eventLimit=500
d.eventLimit = 500
}else{
d.eventLimit = parseInt(d.eventLimit);
}
if(!d.eventStartDate&&d.startDate){
d.eventStartDate=d.startDate
d.eventStartDate = s.stringToSqlTime(d.startDate)
}
if(!d.eventEndDate&&d.endDate){
d.eventEndDate=d.endDate
d.eventEndDate = s.stringToSqlTime(d.endDate)
}
var monitorQuery = ''
var monitorValues = []
@ -3932,15 +4062,13 @@ var tx;
var eventQuery = 'SELECT * FROM Events WHERE ke=?';
var eventQueryValues = [cn.ke];
if(d.eventStartDate&&d.eventStartDate!==''){
d.eventStartDate=d.eventStartDate.replace('T',' ')
if(d.eventEndDate&&d.eventEndDate!==''){
d.eventEndDate=d.eventEndDate.replace('T',' ')
eventQuery+=' AND `time` >= ? AND `time` <= ?';
eventQueryValues.push(decodeURIComponent(d.eventStartDate))
eventQueryValues.push(decodeURIComponent(d.eventEndDate))
eventQueryValues.push(d.eventStartDate)
eventQueryValues.push(d.eventEndDate)
}else{
eventQuery+=' AND `time` >= ?';
eventQueryValues.push(decodeURIComponent(d.eventStartDate))
eventQueryValues.push(d.eventStartDate)
}
}
if(monitorValues.length>0){
@ -3969,10 +4097,10 @@ var tx;
eventQuery.push()
}
if(!d.videoStartDate&&d.startDate){
d.videoStartDate=d.startDate
d.videoStartDate = s.stringToSqlTime(d.startDate)
}
if(!d.videoEndDate&&d.endDate){
d.videoEndDate=d.endDate
d.videoEndDate = s.stringToSqlTime(d.endDate)
}
var getVideos = function(callback){
var videoQuery='SELECT * FROM Videos WHERE ke=?';
@ -3986,19 +4114,15 @@ var tx;
}
switch(true){
case(d.videoStartDate&&d.videoStartDate!==''&&d.videoEndDate&&d.videoEndDate!==''):
d.videoStartDate=d.videoStartDate.replace('T',' ')
d.videoEndDate=d.videoEndDate.replace('T',' ')
videoQuery+=' AND `time` '+d.videoStartDateOperator+' ? AND `end` '+d.videoEndDateOperator+' ?';
videoQueryValues.push(d.videoStartDate)
videoQueryValues.push(d.videoEndDate)
break;
case(d.videoStartDate&&d.videoStartDate!==''):
d.videoStartDate=d.videoStartDate.replace('T',' ')
videoQuery+=' AND `time` '+d.videoStartDateOperator+' ?';
videoQueryValues.push(d.videoStartDate)
break;
case(d.videoEndDate&&d.videoEndDate!==''):
d.videoEndDate=d.videoEndDate.replace('T',' ')
videoQuery+=' AND `end` '+d.videoEndDateOperator+' ?';
videoQueryValues.push(d.videoEndDate)
break;
@ -4406,10 +4530,24 @@ var tx;
d.value=d.value.concat([d.ke,d.$uid])
s.sqlQuery("UPDATE Users SET "+d.condition.join(',')+" WHERE ke=? AND uid=?",d.value)
s.tx({f:'edit_sub_account',ke:d.ke,uid:d.$uid,mail:d.mail,form:d.form},'ADM_'+d.ke);
s.sqlQuery("SELECT * FROM API WHERE ke=? AND uid=?",[d.ke,d.$uid],function(err,rows){
if(rows && rows[0]){
rows.forEach(function(row){
delete(s.api[row.code])
})
}
})
break;
case'delete':
s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[d.$uid,d.ke,d.mail])
s.sqlQuery('DELETE FROM API WHERE uid=? AND ke=?',[d.$uid,d.ke])
s.sqlQuery("SELECT * FROM API WHERE ke=? AND uid=?",[d.ke,d.$uid],function(err,rows){
if(rows && rows[0]){
rows.forEach(function(row){
delete(s.api[row.code])
})
s.sqlQuery('DELETE FROM API WHERE uid=? AND ke=?',[d.$uid,d.ke])
}
})
s.tx({f:'delete_sub_account',ke:d.ke,uid:d.$uid,mail:d.mail},'ADM_'+d.ke);
break;
}
@ -4569,7 +4707,7 @@ var tx;
}
s.log({ke:cn.ke,mid:'$USER'},{type:lang['Websocket Disconnected'],msg:{mail:s.group[cn.ke].users[cn.auth].mail,id:cn.uid,ip:cn.ip}})
delete(s.group[cn.ke].users[cn.auth]);
delete(s.group[cn.ke].dashcamUsers[cn.auth]);
if(s.group[cn.ke].dashcamUsers && s.group[cn.ke].dashcamUsers[cn.auth])delete(s.group[cn.ke].dashcamUsers[cn.auth]);
}
}
if(cn.pluginEngine){
@ -5637,7 +5775,11 @@ app.get(['/:auth/videos/:ke','/:auth/videos/:ke/:id'], function (req,res){
res.setHeader('Content-Type', 'application/json');
res.header("Access-Control-Allow-Origin",req.headers.origin);
s.auth(req.params,function(user){
if(user.permissions.watch_videos==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.video_view.indexOf(req.params.id)===-1){
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.s([]))
return
}
@ -5667,6 +5809,12 @@ app.get(['/:auth/videos/:ke','/:auth/videos/:ke/:id'], function (req,res){
}
}
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='>='
}
@ -5675,8 +5823,6 @@ app.get(['/:auth/videos/:ke','/:auth/videos/:ke/:id'], function (req,res){
}
switch(true){
case(req.query.start&&req.query.start!==''&&req.query.end&&req.query.end!==''):
req.query.start=req.query.start.replace('T',' ')
req.query.end=req.query.end.replace('T',' ')
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `end` '+req.query.endOperator+' ?';
req.count_sql+=' AND `time` '+req.query.startOperator+' ? AND `end` '+req.query.endOperator+' ?';
req.ar.push(req.query.start)
@ -5685,14 +5831,12 @@ app.get(['/:auth/videos/:ke','/:auth/videos/:ke/:id'], function (req,res){
req.count_ar.push(req.query.end)
break;
case(req.query.start&&req.query.start!==''):
req.query.start=req.query.start.replace('T',' ')
req.sql+=' AND `time` '+req.query.startOperator+' ?';
req.count_sql+=' AND `time` '+req.query.startOperator+' ?';
req.ar.push(req.query.start)
req.count_ar.push(req.query.start)
break;
case(req.query.end&&req.query.end!==''):
req.query.end=req.query.end.replace('T',' ')
req.sql+=' AND `end` '+req.query.endOperator+' ?';
req.count_sql+=' AND `end` '+req.query.endOperator+' ?';
req.ar.push(req.query.end)
@ -5712,17 +5856,17 @@ app.get(['/:auth/videos/:ke','/:auth/videos/:ke/:id'], function (req,res){
res.end(s.s({total:0,limit:req.query.limit,skip:0,videos:[]}, null, 3));
return
}
s.sqlQuery(req.count_sql,req.count_ar,function(err,count){
s.video('linkBuild',r,req.params.auth)
if(req.query.limit.indexOf(',')>-1){
req.skip=parseInt(req.query.limit.split(',')[0])
req.query.limit=parseInt(req.query.limit.split(',')[0])
}else{
req.skip=0
req.query.limit=parseInt(req.query.limit)
}
res.end(s.s({isUTC:config.useUTC,total:count[0]['COUNT(*)'],limit:req.query.limit,skip:req.skip,videos:r}, null, 3));
})
s.sqlQuery(req.count_sql,req.count_ar,function(err,count){
s.video('linkBuild',r,req.params.auth)
if(req.query.limit.indexOf(',')>-1){
req.skip=parseInt(req.query.limit.split(',')[0])
req.query.limit=parseInt(req.query.limit.split(',')[0])
}else{
req.skip=0
req.query.limit=parseInt(req.query.limit)
}
res.end(s.s({isUTC:config.useUTC,total:count[0]['COUNT(*)'],limit:req.query.limit,skip:req.skip,videos:r}, null, 3));
})
})
},res,req);
});
@ -5755,9 +5899,9 @@ app.get(['/:auth/events/:ke','/:auth/events/:ke/:id','/:auth/events/:ke/:id/:lim
}
}
if(req.params.start&&req.params.start!==''){
req.params.start=req.params.start.replace('T',' ')
req.params.start = s.stringToSqlTime(req.params.start)
if(req.params.end&&req.params.end!==''){
req.params.end=req.params.end.replace('T',' ')
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))
@ -5788,7 +5932,7 @@ app.get(['/:auth/logs/:ke','/:auth/logs/:ke/:id'], function (req,res){
res.setHeader('Content-Type', 'application/json');
res.header("Access-Control-Allow-Origin",req.headers.origin);
s.auth(req.params,function(user){
if(user.permissions.get_logs==="0"){
if(user.permissions.get_logs==="0" || user.details.sub && user.details.view_logs !== '1'){
res.end(s.s([]))
return
}
@ -5818,13 +5962,13 @@ app.get(['/:auth/logs/:ke','/:auth/logs/:ke/:id'], function (req,res){
req.query.endOperator='<='
}
if(req.query.start && req.query.start !== '' && req.query.end && req.query.end !== ''){
req.query.start=req.query.start.replace('T',' ')
req.query.end=req.query.end.replace('T',' ')
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=req.query.start.replace('T',' ')
req.query.start = s.stringToSqlTime(req.query.start)
req.sql+=' AND `time` '+req.query.startOperator+' ?';
req.ar.push(req.query.start)
}
@ -5886,7 +6030,8 @@ app.all(['/:auth/configureMonitor/:ke/:id','/:auth/configureMonitor/:ke/:id/:f']
res.setHeader('Content-Type', 'application/json');
res.header("Access-Control-Allow-Origin",req.headers.origin);
s.auth(req.params,function(user){
if(req.params.f!=='delete'){
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
if(req.params.f !== 'delete'){
if(!req.body.data&&!req.query.data){
req.ret.msg='No Monitor Data found.'
res.end(s.s(req.ret, null, 3))
@ -5905,7 +6050,10 @@ app.all(['/:auth/configureMonitor/:ke/:id','/:auth/configureMonitor/:ke/:id/:f']
}
return
}
if(!user.details.sub||user.details.allmonitors==='1'||user.details.monitor_edit.indexOf(req.monitor.mid)>-1){
if(!user.details.sub ||
user.details.allmonitors === '1' ||
hasRestrictions && user.details.monitor_edit.indexOf(req.monitor.mid) >- 1 ||
hasRestrictions && user.details.monitor_create === '1'){
if(req.monitor&&req.monitor.mid&&req.monitor.name){
req.set=[],req.ar=[];
req.monitor.mid=req.params.id.replace(/[^\w\s]/gi,'').replace(/ /g,'');
@ -6148,6 +6296,94 @@ app.get('/:auth/fileBin/:ke/:id/:year/:month/:day/:file', function (req,res){
}
s.auth(req.params,req.fn,res,req);
});
//zip videos and get link from fileBin
app.get('/:auth/zipVideos/:ke', function (req,res){
res.header("Access-Control-Allow-Origin",req.headers.origin);
var failed = function(resp){
res.setHeader('Content-Type', 'application/json');
res.end(s.s(resp))
}
if(req.query.videos && req.query.videos !== ''){
s.auth(req.params,function(user){
var videosSelected = JSON.parse(req.query.videos)
var where = []
var values = []
videosSelected.forEach(function(video){
where.push("(ke=? AND mid=? AND `time`=?)")
if(!video.ke)video.ke = req.params.ke
values.push(video.ke)
values.push(video.mid)
var time = s.nameToTime(video.filename)
if(req.query.isUTC === 'true'){
time = s.utcToLocal(time)
}
time = new Date(time)
values.push(time)
})
s.sqlQuery('SELECT * FROM Videos WHERE '+where.join(' OR '),values,function(err,r){
var resp = {ok:false}
if(r && r[0]){
resp.ok = true
var zipDownload = null
var tempFiles = []
var fileId = s.gid()
var fileBinDir = s.dir.fileBin+req.params.ke+'/'
var tempScript = s.dir.streams+req.params.ke+'/'+fileId+'.sh'
var zippedFilename = s.formattedTime()+'-'+fileId+'-Shinobi_Recordings.zip'
var zippedFile = fileBinDir+zippedFilename
var script = 'cd '+fileBinDir+' && zip -9 -r '+zippedFile
res.on('close', () => {
if(zipDownload && zipDownload.destroy){
zipDownload.destroy()
}
fs.unlink(zippedFile);
})
if(!fs.existsSync(fileBinDir)){
fs.mkdirSync(fileBinDir);
}
r.forEach(function(video){
timeFormatted = s.formattedTime(video.time)
video.filename = timeFormatted+'.'+video.ext
var dir = s.video('getDir',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.log({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(){})
})
res.setHeader('Content-Disposition', 'attachment; filename="'+zippedFilename+'"')
var zipDownload = fs.createReadStream(zippedFile)
zipDownload.pipe(res)
zipDownload.on('error', function (error) {
s.log({ke:req.params.ke,mid:'$USER'},{title:'Zip Download Error',msg:error.toString()})
if(zipDownload && zipDownload.destroy){
zipDownload.destroy()
}
});
zipDownload.on('close', function () {
res.end()
zipDownload.destroy();
fs.unlinkSync(zippedFile);
});
})
}else{
failed({ok:false,msg:'No Videos Found'})
}
})
},res,req);
}else{
failed({ok:false,msg:'"videos" query variable is missing from request.'})
}
});
// Get video file
app.get('/:auth/videos/:ke/:id/:file', function (req,res){
s.auth(req.params,function(user){
@ -6160,7 +6396,7 @@ app.get('/:auth/videos/:ke/:id/:file', function (req,res){
time = s.utcToLocal(time)
}
time = new Date(time)
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND mid=? AND time=?',[req.params.ke,req.params.id,time],function(err,r){
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND mid=? AND `time`=?',[req.params.ke,req.params.id,time],function(err,r){
if(r&&r[0]){
req.dir=s.video('getDir',r[0])+req.params.file
if (fs.existsSync(req.dir)){
@ -6256,7 +6492,7 @@ app.get(['/:auth/videos/:ke/:id/:file/:mode','/:auth/videos/:ke/:id/:file/:mode/
time = s.utcToLocal(time)
}
time = new Date(time)
req.sql='SELECT * FROM Videos WHERE ke=? AND mid=? AND time=?';
req.sql='SELECT * FROM Videos WHERE ke=? AND mid=? AND `time`=?';
req.ar=[req.params.ke,req.params.id,time];
s.sqlQuery(req.sql,req.ar,function(err,r){
if(r&&r[0]){
@ -6273,7 +6509,7 @@ app.get(['/:auth/videos/:ke/:id/:file/:mode','/:auth/videos/:ke/:id/:file/:mode/
req.ret.msg='Not a valid value.';
}else{
req.ret.ok=true;
s.sqlQuery('UPDATE Videos SET status=? WHERE ke=? AND mid=? AND time=?',[req.params.f,req.params.ke,req.params.id,time])
s.sqlQuery('UPDATE Videos SET status=? WHERE ke=? AND mid=? AND `time`=?',[req.params.f,req.params.ke,req.params.id,time])
s.tx(r,'GRP_'+r.ke);
}
break;
@ -6890,6 +7126,7 @@ if(config.childNodes.mode === 'child'){
}
s.systemLog(v.mail+' : '+lang.startUpText0+' : '+rr.length,v.size)
s.init('group',v)
s.init('apps',v)
s.systemLog(v.mail+' : '+lang.startUpText1,countFinished+'/'+count)
if(countFinished===count){
s.systemLog(lang.startUpText4)

View File

@ -104,6 +104,8 @@
"Can View Streams": "Can View Streams",
"Can View Videos": "Can View Videos",
"Can View Monitor": "Can View Monitor",
"Can Change User Settings": "Can Change User Settings",
"Can Create and Delete Monitors": "Can Create and Delete Monitors",
"Can Edit Monitor": "Can Edit Monitor",
"Can Delete Videos": "Can Delete Videos",
"Delete Video": "Delete Video",
@ -178,6 +180,7 @@
"Monitor Groups": "Monitor Groups",
"Group Name": "Group Name",
"WebDAV": "WebDAV",
"Discord Bot": "Discord Bot",
"URL": "URL",
"Autosave": "Autosave",
"Save Directory": "Save Directory",
@ -186,6 +189,7 @@
"Monitors per row": "Monitors per row <small>for Montage</small>",
"Browser Console Log": "Browser Console Log",
"Log Stream": "Log Stream",
"Privileges": "Privileges",
"All Monitors and Privileges": "All Monitors and Privileges",
"Permissions": "Permissions",
"Time-lapse Tool": "Time-lapse Tool",
@ -211,9 +215,13 @@
"Set to Watch Only": "Set to Watch Only",
"Save as": "Save as",
"Add New": "Add New",
"Zip and Download": "Zip and Download",
"Export Selected Videos": "Export Selected Videos",
"Delete Selected Videos": "Delete Selected Videos",
"DeleteSelectedVideosMsg": "Do you want to delete these videos? You cannot recover them.",
"ExportSelectedVideosMsg": "Do you want to export these videos? It may take some time to zip and download.",
"clientStreamFailedattemptingReconnect": "Client side ctream check failed, attempting reconnect.",
"Export Video": "Export Video",
"Delete Filter": "Delete Filter",
"confirmDeleteFilter": "Do you want to delete this filter? You cannot recover it.",
"Fix Video": "Fix Video",
@ -249,6 +257,7 @@
"Unable to Launch": "Unable to Launch",
"UnabletoLaunchText": "Please save new monitor first. Then attempt to launch the region editor.",
"NoVideosFoundForDateRange": "No Videos found in this date range. Try setting the start date further back.",
"NoLogsFoundForDateRange": "No Logs found in this date range. Try widening the date range.",
"monitorEditFailedMaxReached": "Your account has reached the maximum number of cameras that can be created. Speak to an administrator if you would like this changed.",
"in": "in",
"ago": "ago",
@ -369,7 +378,9 @@
"Allow Next Trigger": "Allow Next Trigger <small>in Milliseconds</small>",
"Save Events to SQL": "Save Events to SQL",
"Email on Trigger": "Email on Trigger <small>Emails go to the main account holder's login address.</small>",
"Discord Alert on Trigger": "Discord Alert on Trigger",
"Allow Next Email": "Allow Next Email <small>in Minutes</small>",
"Allow Next Discord Alert": "Allow Next Discord Alert <small>in Minutes</small>",
"How to Record": "How to Record",
"Trigger Record": "Trigger Record",
"Recording Timeout": "Recording Timeout <small>in Minutes</small>",
@ -432,6 +443,8 @@
"libx264": "libx264",
"libx265": "libx265",
"copy": "copy",
"Audio": "Audio",
"Mute Audio": "Mute Audio",
"No Audio": "No Audio",
"aac": "aac",
"ac3": "ac3",
@ -576,6 +589,8 @@
"Preview":"Preview",
"Websocket Connected":"Websocket Connected",
"Websocket Disconnected":"Websocket Disconnected",
"Token":"Token",
"Channel ID":"Channel ID",
"New Authentication Token":"New Authentication Token",
"All Logs":"All Logs",
"For Group":"For Group",
@ -588,8 +603,10 @@
"in Days":"in Days",
"Can edit how long to keep Logs":"Can edit how long to keep Logs",
"Can use Admin Panel":"Can use Admin Panel",
"Can use Discord Bot":"Can use Discord Bot",
"Can use WebDAV":"Can use WebDAV",
"Can use LDAP":"Can use LDAP",
"Can View Logs":"Can View Logs",
"Can edit how long to keep Events":"Can edit how long to keep Events",
"Leave blank for unlimited":"Leave blank for unlimited",
"Limited":"Limited",

View File

@ -12,6 +12,20 @@ $.ccio={
fr:$('#files_recent'),
mon:{}
};
$.ccio.permissionCheck = function(toCheck,monitorId){
var details = $user.details
if(details.sub && details.allmonitors === '0'){
var chosenValue = details[toCheck]
if(details[toCheck] instanceof Array && chosenValue.indexOf(monitorId) > -1){
return true
}else if(chosenValue === '1'){
return true
}
}else{
return true
}
return false
}
$.ccio.downloadJSON = function(jsonToDownload,filename,errorResponse){
var arr = jsonToDownload;
if(arr.length===0 && errorResponse){
@ -899,7 +913,46 @@ switch($user.details.lang){
break;
case 1://monitor icon
d.src=placeholder.getData(placeholder.plcimg({bgcolor:'#b57d00',text:'...'}));
tmp+='<div auth="'+user.auth_token+'" mid="'+d.mid+'" ke="'+d.ke+'" title="'+d.mid+' : '+d.name+'" class="monitor_block glM'+d.mid+user.auth_token+' col-md-4"><img monitor="watch" class="snapshot" src="'+d.src+'"><div class="box"><div class="title monitor_name truncate">'+d.name+'</div><div class="list-data"><div class="monitor_mid">'+d.mid+'</div><div><b><%-cleanLang(lang['Save as'])%> :</b> <span class="monitor_ext">'+d.ext+'</span></div><div><b>Status :</b> <span class="monitor_status">'+d.status+'</span></div></div><div class="icons text-center"><div class="btn-group"><a class="btn btn-xs btn-default permission_monitor_edit" monitor="edit"><i class="fa fa-wrench"></i></a> <a monitor="videos_table" class="btn btn-xs btn-default"><i class="fa fa-film"></i></a> <a monitor="pop" class="btn btn-xs btn-success"><i class="fa fa-external-link"></i></a></div></div></div></div>';
tmp+='<div auth="'+user.auth_token+'" mid="'+d.mid+'" ke="'+d.ke+'" title="'+d.mid+' : '+d.name+'" class="monitor_block glM'+d.mid+user.auth_token+' col-md-4"><img monitor="watch" class="snapshot" src="'+d.src+'"><div class="box"><div class="title monitor_name truncate">'+d.name+'</div><div class="list-data"><div class="monitor_mid">'+d.mid+'</div><div><b><%-cleanLang(lang['Save as'])%> :</b> <span class="monitor_ext">'+d.ext+'</span></div><div><b>Status :</b> <span class="monitor_status">'+d.status+'</span></div></div><div class="icons text-center">'
tmp+='<div class="btn-group btn-group-xs">'
var buttons = {
"Pop": {
"label": "Pop",
"attr": "monitor=\"pop\"",
"class": "default",
"icon": "external-link"
},
"Power Viewer": {
"label": "Power Viewer",
"attr": "monitor=\"powerview\"",
"class": "default",
"icon": "map-marker"
},
"Videos List": {
"label": "Videos List",
"attr": "monitor=\"videos_table\"",
"class": "default",
"icon": "film"
},
"Monitor Settings": {
"label": "Monitor Settings",
"attr": "monitor=\"edit\"",
"class": "default",
"icon": "wrench"
}
}
if(!$.ccio.permissionCheck('video_view',d.mid)){
delete(buttons["Videos List"])
delete(buttons["Power Viewer"])
}
if(!$.ccio.permissionCheck('monitor_edit',d.mid)){
delete(buttons["Monitor Settings"])
}
$.each(buttons,function(n,v){
tmp+='<a class="btn btn-'+v.class+'" '+v.attr+' title="'+v.label+'"><i class="fa fa-'+v.icon+'"></i></a>'
})
tmp+='</div>\
</div></div></div>';
delete(d.src);
break;
case 2://monitor stream
@ -922,23 +975,90 @@ switch($user.details.lang){
tmp+='<div><span class="monitor_name">'+d.name+'</span><span class="monitor_not_record_copy">, <%-cleanLang(lang['Recording FPS'])%> : <span class="monitor_fps">'+d.fps+'</span></span></div>';
tmp+='</div>';
tmp+='<div class="btn-group btn-group-sm">'//start of btn list
$.each([
{label:"<%-cleanLang(lang.Snapshot)%>",attr:'monitor="snapshot"',class:'primary',icon:'camera'},
{label:"<%-cleanLang(lang['Show Logs'])%>",attr:'monitor="show_data"',class:'warning',icon:'exclamation-triangle'},
// {label:"<%-cleanLang(lang['Show Logs'])%>",attr:'class_toggle="show_data" data-target="'+dataTarget+'"',class:'warning',icon:'exclamation-triangle'},
{label:"<%-cleanLang(lang.Control)%>",attr:'monitor="control_toggle"',class:'default arrows',icon:'arrows'},
{label:"<%-cleanLang(lang['Status Indicator'])%>",attr:'monitor="watch_on"',class:'success signal',icon:'plug'},
{label:"<%-cleanLang(lang['Detector'])%>",attr:'monitor="motion"',class:'warning',icon:'grav'},
{label:"<%-cleanLang(lang.Pop)%>",attr:'monitor="pop"',class:'default',icon:'external-link'},
// {label:"<%-cleanLang(lang.Magnify)%>",attr:'monitor="magnify"',class:'default',icon:'search-plus'},
{label:"<%-cleanLang(lang.Calendar)%>",attr:'monitor="calendar"',class:'default',icon:'calendar'},
{label:"<%-cleanLang(lang['Power Viewer'])%>",attr:'monitor="powerview"',class:'default',icon:'map-marker'},
{label:"<%-cleanLang(lang['Time-lapse'])%>",attr:'monitor="timelapse"',class:'default',icon:'angle-double-right'},
{label:"<%-cleanLang(lang['Videos List'])%>",attr:'monitor="videos_table"',class:'default',icon:'film'},
{label:"<%-cleanLang(lang['Monitor Settings'])%>",attr:'monitor="edit"',class:'default permission_monitor_edit',icon:'wrench'},
{label:"<%-cleanLang(lang.Fullscreen)%>",attr:'monitor="fullscreen"',class:'default',icon:'arrows-alt'},
{label:"<%-cleanLang(lang.Close)%>",attr:'monitor="watch_off"',class:'danger',icon:'times'},
],function(n,v){
var buttons = {
"Snapshot": {
"label": "Snapshot",
"attr": "monitor=\"snapshot\"",
"class": "primary",
"icon": "camera"
},
"Show Logs": {
"label": "Show Logs",
"attr": "monitor=\"show_data\"",
"class": "warning",
"icon": "exclamation-triangle"
},
"Control": {
"label": "Control",
"attr": "monitor=\"control_toggle\"",
"class": "default arrows",
"icon": "arrows"
},
"Status Indicator": {
"label": "Status Indicator",
"attr": "monitor=\"watch_on\"",
"class": "success signal",
"icon": "plug"
},
"Pop": {
"label": "Pop",
"attr": "monitor=\"pop\"",
"class": "default",
"icon": "external-link"
},
"Calendar": {
"label": "Calendar",
"attr": "monitor=\"calendar\"",
"class": "default ",
"icon": "calendar"
},
"Power Viewer": {
"label": "Power Viewer",
"attr": "monitor=\"powerview\"",
"class": "default",
"icon": "map-marker"
},
"Time-lapse": {
"label": "Time-lapse",
"attr": "monitor=\"timelapse\"",
"class": "default",
"icon": "angle-double-right"
},
"Videos List": {
"label": "Videos List",
"attr": "monitor=\"videos_table\"",
"class": "default",
"icon": "film"
},
"Monitor Settings": {
"label": "Monitor Settings",
"attr": "monitor=\"edit\"",
"class": "default",
"icon": "wrench"
},
"Fullscreen": {
"label": "Fullscreen",
"attr": "monitor=\"fullscreen\"",
"class": "default",
"icon": "arrows-alt"
},
"Close": {
"label": "Close",
"attr": "monitor=\"watch_off\"",
"class": "danger",
"icon": "times"
}
}
if(!$.ccio.permissionCheck('video_view',d.mid)){
delete(buttons["Videos List"])
delete(buttons["Time-lapse"])
delete(buttons["Power Viewer"])
delete(buttons["Calendar"])
}
if(!$.ccio.permissionCheck('monitor_edit',d.mid)){
delete(buttons["Monitor Settings"])
}
$.each(buttons,function(n,v){
tmp+='<a class="btn btn-'+v.class+'" '+v.attr+' title="'+v.label+'"><i class="fa fa-'+v.icon+'"></i></a>'
})
tmp+='</div>';//end of btn list
@ -1015,6 +1135,11 @@ switch($user.details.lang){
}
}
k.e.append(tmp).find('.stream-element').resize();
if($.ccio.op().switches.monitorMuteAudio === 1){
k.e.find('video').each(function(n,el){
el.muted = "muted"
})
}
break;
case'user-row':
d.e=$('.user-row[uid="'+d.uid+'"][ke="'+d.ke+'"]')
@ -1802,6 +1927,7 @@ $.ccio.globalWebsocket=function(d,user){
delete($.timelapse.currentVideosArray.videos[$.timelapse.currentVideos[d.filename].position])
$.timelapse.drawTimeline(false)
}
if($.vidview.loadedVideos && $.vidview.loadedVideos[d.filename])delete($.vidview.loadedVideos[d.filename])
break;
case'video_build_success':
if(!d.mid){d.mid=d.id;};d.status=1;
@ -2519,6 +2645,14 @@ $.ccio.cx=function(x,user){
$(document).ready(function(e){
console.log("%cWarning!", "font: 2em monospace; color: red;");
console.log('%cLeaving the developer console open is fine if you turn off "Network Recording". This is because it will keep a log of all files, including frames and videos segments.', "font: 1.2em monospace; ");
if(!$.ccio.permissionCheck('monitor_create')){
$('#add_monitor_button_main').remove()
}
$.each(['user_change','monitor_create','view_logs'],function(n,permission){
if(!$.ccio.permissionCheck(permission)){
$('.permission_'+permission).remove()
}
})
//global form functions
$.ccio.form={};
$.ccio.form.details=function(e){
@ -3084,7 +3218,7 @@ $.multimon.e.on('shown.bs.modal',function() {
tmp+='<td><div class="checkbox"><input id="multimonCheck_'+v.ke+v.mid+v.user.auth_token+'" type="checkbox" name="'+v.ke+v.mid+v.user.auth_token+'" value="1"><label for="multimonCheck_'+v.ke+v.mid+v.user.auth_token+'"></label></div></td>'
tmp+='<td><a monitor="watch"><img class="small-square-img" src="'+img+'"></a></td><td>'+v.name+'<br><small>'+v.mid+'</small></td><td class="monitor_status">'+v.status+'</td><td>'+streamURL+'</td>'
//buttons
tmp+='<td class="text-right"><a title="<%-cleanLang(lang.Pop)%>" monitor="pop" class="btn btn-primary"><i class="fa fa-external-link"></i></a> <a title="<%-cleanLang(lang.Calendar)%>" monitor="calendar" class="btn btn-default"><i class="fa fa-calendar"></i></a> <a title="<%-cleanLang(lang['Power Viewer'])%>" class="btn btn-default" monitor="powerview"><i class="fa fa-map-marker"></i></a> <a title="<%-cleanLang(lang['Time-lapse'])%>" class="btn btn-default" monitor="timelapse"><i class="fa fa-angle-double-right"></i></a> <a title="<%-cleanLang(lang['Videos List'])%>" monitor="videos_table" class="btn btn-default"><i class="fa fa-film"></i></a> <a title="<%-cleanLang(lang['Monitor Settings'])%>" class="btn btn-default permission_monitor_edit" monitor="edit"><i class="fa fa-wrench"></i></a></td>'
tmp+='<td class="text-right"><a title="<%-cleanLang(lang.Pop)%>" monitor="pop" class="btn btn-primary"><i class="fa fa-external-link"></i></a> <a title="<%-cleanLang(lang.Calendar)%>" monitor="calendar" class="btn btn-default"><i class="fa fa-calendar"></i></a> <a title="<%-cleanLang(lang['Power Viewer'])%>" class="btn btn-default" monitor="powerview"><i class="fa fa-map-marker"></i></a> <a title="<%-cleanLang(lang['Time-lapse'])%>" class="btn btn-default" monitor="timelapse"><i class="fa fa-angle-double-right"></i></a> <a title="<%-cleanLang(lang['Videos List'])%>" monitor="videos_table" class="btn btn-default"><i class="fa fa-film"></i></a> <a title="<%-cleanLang(lang['Monitor Settings'])%>" class="btn btn-default" monitor="edit"><i class="fa fa-wrench"></i></a></td>'
tmp+='</tr>'
})
$.multimon.table.html(tmp)
@ -4068,27 +4202,78 @@ $.vidview.f.submit(function(e){
$('#videos_viewer_limit,#videos_viewer_daterange').change(function(){
$.vidview.f.submit()
})
$.vidview.e.find('.delete_selected').click(function(e){
e.s={}
$.vidview.getSelected = function(getArray){
var arr = {}
if(getArray){
arr = []
}
$.vidview.f.find('[data-ke] input:checked').each(function(n,v){
v=$(v).parents('tr')
e.s[v.attr('data-file')]={mid:v.attr('data-mid'),auth:v.attr('data-auth')}
if(getArray){
arr.push({filename:v.attr('data-file'),mid:v.attr('data-mid'),auth:v.attr('data-auth')})
}else{
arr[v.attr('data-file')]={mid:v.attr('data-mid'),auth:v.attr('data-auth')}
}
})
return arr
}
$.vidview.e.find('.delete_selected').click(function(){
e = {}
e.s = $.vidview.getSelected()
if(Object.keys(e.s).length === 0){
$.ccio.init('note',{
title:'No Videos Selected',
text:'You must choose at least one video.',
type:'error'
},$user);
return
}
$.confirm.e.modal('show');
$.confirm.title.text('<%-cleanLang(lang['Delete Selected Videos'])%>')
e.html='<%-cleanLang(lang.DeleteSelectedVideosMsg)%><div style="margin-bottom:15px"></div>'
var deleteLinks = []
$.each(e.s,function(n,v){
e.html+=n+'<br>';
if($.vidview.loadedVideos[n])deleteLinks.push($.vidview.loadedVideos[n].links.deleteVideo)
})
$.confirm.body.html(e.html)
$.confirm.click({title:'Delete Video',class:'btn-danger'},function(){
$.each(e.s,function(n,v){
$.getJSON($.ccio.init('location',$.users[v.auth])+v.auth+'/videos/'+$user.ke+'/'+v.mid+'/'+n+'/delete',function(d){
$.each(deleteLinks,function(n,link){
$.getJSON(link,function(d){
$.ccio.log(d)
})
})
});
})
$.vidview.e.find('.export_selected').click(function(){
e = {}
var videos = $.vidview.getSelected(true)
if(videos.length === 0){
$.ccio.init('note',{
title:'No Videos Selected',
text:'You must choose at least one video.',
type:'error'
},$user);
return
}
$.confirm.e.modal('show');
$.confirm.title.text('<%-cleanLang(lang['Export Selected Videos'])%>')
var html = '<%-cleanLang(lang.ExportSelectedVideosMsg)%><div style="margin-bottom:15px"></div>'
$.each(videos,function(n,v){
html+=v.filename+'<br>';
})
$.confirm.body.html(html)
$.confirm.click({title:'Export Video',class:'btn-danger'},function(){
var queryVariables = []
queryVariables.push('videos='+JSON.stringify(videos))
if(<%-config.useUTC%> === true){
queryVariables.push('isUTC=true')
}
console.log(queryVariables)
var downloadZip = $.ccio.init('location',$user)+$user.auth_token+'/zipVideos/'+$user.ke+'?'+queryVariables.join('&')
$('#temp').html('<iframe>a</iframe>').find('iframe').attr('src',downloadZip);
});
})
$.vidview.pages.on('click','[page]',function(e){
e.limit=$.vidview.limit.val();
e.page=$(this).attr('page');
@ -4948,6 +5133,15 @@ $('body')
$('.monitor_item').attr('data-gs-auto-position','no')
}
break;
case'monitorMuteAudio':
$('.monitor_item video').each(function(n,el){
if(e.o[e.switch] === 1){
el.muted = true
}else{
el.muted = false
}
})
break;
}
switch(e.e.attr('type')){
case'text':
@ -5161,6 +5355,7 @@ $('body')
d.fn()
$.vidview.pages.find('[page="'+$.vidview.current_page+'"]').addClass('active')
e.v=$.vidview.e;
$.vidview.loadedVideos = {}
e.b=e.v.modal('show').find('.modal-body .contents');
e.t=e.v.find('.modal-title i');
switch(e.a){
@ -5169,7 +5364,8 @@ $('body')
e.ar=[];
if(d.videos[0]){
$.each(d.videos,function(n,v){
if(v.status!==0){
if(v.status !== 0){
$.vidview.loadedVideos[v.filename] = Object.assign(v,{})
var n=$.ccio.mon[v.ke+v.mid+user.auth_token];
if(n){v.title=n.name+' - '+(parseInt(v.size)/1000000).toFixed(2)+'mb';}
v.start=v.time;
@ -5221,13 +5417,14 @@ $('body')
e.tmp+='<tbody>';
$.each(d.videos,function(n,v){
if(v.status!==0){
$.vidview.loadedVideos[v.filename] = Object.assign(v,{})
var href = $.ccio.init('videoUrlBuild',v)
v.mon=$.ccio.mon[v.ke+v.mid+user.auth_token];
v.start=v.time;
// v.filename=$.ccio.init('tf',v.time)+'.'+v.ext;
e.tmp+='<tr data-ke="'+v.ke+'" data-status="'+v.status+'" data-mid="'+v.mid+'" data-file="'+v.filename+'" data-auth="'+v.mon.user.auth_token+'">';
e.tmp+='<td><div class="checkbox"><input id="'+v.ke+'_'+v.filename+'" name="'+v.filename+'" value="'+v.mid+'" type="checkbox"><label for="'+v.ke+'_'+v.filename+'"></label></div></td>';
e.tmp+='<td><span class="livestamp" title="'+v.end+'"></span></td>';
e.tmp+='<td><span class="livestamp" title="'+$.ccio.timeObject(v.end).format('YYYY-MM-DD HH:mm:ss')+'"></span></td>';
e.tmp+='<td title="'+v.end+'">'+$.ccio.timeObject(v.end).format('h:mm:ss A, MMMM Do YYYY')+'</td>';
e.tmp+='<td title="'+v.time+'">'+$.ccio.timeObject(v.time).format('h:mm:ss A, MMMM Do YYYY')+'</td>';
e.tmp+='<td>'+v.mon.name+'</td>';

View File

@ -181,7 +181,9 @@ $.sU.e.on('click','.permission',function(e){
$.each($.ccio.subs[$.pR.user],function(n,v){
$.pR.e.find('[name="'+n+'"]').val(v)
})
$.pR.e.find('[detail="allmonitors"]').val(e.d.allmonitors).change()
$.pR.e.find('[detail]').each(function(n,v){
$(v).val(e.d[$(v).attr('detail')])
}).first().change()
$.each(['monitors','monitor_edit','video_delete','video_view'],function(m,b){
if(e.d[b]){
$.each(e.d[b],function(n,v){
@ -193,37 +195,43 @@ $.sU.e.on('click','.permission',function(e){
//permission window
$.pR={e:$('#permissions'),l:$('#permissionsLabel small')};$.pR.f=$.pR.e.find('form')
$.pR.e.on('change','[detail]',function(e){
e.f=$(this).parents('form');
var details = $.pR.e.find('[name="details"]')
e.ar = JSON.parse(details.val())
e.f.find('[detail]').each(function(n,v){
v = $(v);e.ar[v.attr('detail')] = v.val()
})
details.val(JSON.stringify(e.ar))
})
$.pR.e.on('change','[detail="allmonitors"]',function(e){
e.e=$(this),
e.mon=$('.permission-view')
e.details=$.pR.e.find('[name="details"]')
e.json=JSON.parse(e.details.val())
if(e.e.val()=='1'){
if(e.e.val() === '1'){
e.mon.hide();
e.json.allmonitors='1';
}else{
e.mon.show()
e.json.allmonitors='0';
$.pR.e.find('[monitor]').first().change()
}
e.details.val(JSON.stringify(e.json))
})
$.pR.e.on('click','[check]',function(e){
$(this).parents('.form-group-group').find('select').val($(this).attr('check')).first().change()
})
$.pR.e.on('change','[monitor]',function(e){
e.monitors=[];
e.key=$(this).attr('monitor');
e.details=$.pR.e.find('[name="details"]')
try{e.detail=JSON.parse(e.details.val())}catch(err){e.detail={}}
if(!e.detail){e.detail={}}
$.pR.e.find('[monitor="'+e.key+'"]').each(function(n,v){
v=$(v)
if(v.val()=='1'){
e.monitors.push(v.attr('mid'))
}
});
e.detail[e.key]=e.monitors;
$.pR.e.find('[monitor]').each(function(n,kel){
var monitors = [];
var key = $(kel).attr('monitor')
$.pR.e.find('[monitor="'+key+'"]').each(function(n,v){
var el = $(v)
if(el.val() === '1'){
monitors.push(el.attr('mid'))
}
});
e.detail[key] = monitors
})
e.details.val(JSON.stringify(e.detail))
});
$.pR.f.submit(function(e){

View File

@ -15,7 +15,7 @@
<script src="libs/js/jquery-ui.min.js"></script>
<script src="libs/js/jquery.serialize.js"></script>
</head>
<% cleanLang=function(string){
<% cleanLang = function(string){
if(!string){string=''}
return string.replace(/'/g,"\\'")
} %>
}%>

View File

@ -119,6 +119,14 @@
</select></div>
</label>
</div>
<div class="form-group h_l_input h_l_limited">
<label><div><span><%-lang['Can use Discord Bot']%></span></div>
<div><select class="form-control" detail="use_discordbot">
<option value="1" selected><%-lang.Yes%></option>
<option value="0"><%-lang.No%></option>
</select></div>
</label>
</div>
<div class="form-group h_l_input h_l_limited">
<label><div><span><%-lang['Can use LDAP']%></span></div>
<div><select class="form-control" detail="use_ldap">

View File

@ -953,20 +953,31 @@
<div><input class="form-control" detail="detector_command_timeout" placeholder="10"></div>
</label>
</div>
<div class="row">
<div class="form-group col-md-12">
<label><div><span><%-lang['Email on Trigger']%></span></div>
<div><select class="form-control" detail="detector_mail">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group col-md-12">
<label><div><span><%-lang['Allow Next Email']%></span></div>
<div><input class="form-control" detail="detector_mail_timeout" placeholder="10"></div>
</label>
</div>
<div class="form-group">
<label><div><span><%-lang['Email on Trigger']%></span></div>
<div><select class="form-control" detail="detector_mail" selector="h_det_email">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group h_det_email_input h_det_email_1">
<label><div><span><%-lang['Allow Next Email']%></span></div>
<div><input class="form-control" detail="detector_mail_timeout" placeholder="10"></div>
</label>
</div>
<div class="form-group">
<label><div><span><%-lang['Discord Alert on Trigger']%></span></div>
<div><select class="form-control" detail="detector_discordbot" selector="h_det_discord">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group h_det_discord_input h_det_discord_1">
<label><div><span><%-lang['Allow Next Discord Alert']%></span></div>
<div><input class="form-control" detail="detector_discordbot_timeout" placeholder="10"></div>
</label>
</div>
<div class="hidden">
<div><input detail="cords" placeholder=""></div>

View File

@ -179,6 +179,31 @@
</div>
</div>
<% } %>
<% if(details.use_discordbot!=='0'){ %>
<div class="form-group-group forestgreen">
<h4><%-lang['Discord Bot']%></h4>
<div class="form-group">
<label><div><span><%-lang.Enabled%></span></div>
<div><select class="form-control" detail="discordbot" selector="u_discord_bot">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="u_discord_bot_input u_discord_bot_1">
<div class="form-group">
<label><div><span><%-lang.Token%></span></div>
<div><input type="password" class="form-control" placeholder="XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXX" detail="discordbot_token"></div>
</label>
</div>
<div class="form-group">
<label><div><span><%-lang['Channel ID']%></span></div>
<div><input class="form-control" placeholder="xxxxxxxxxxxxxxxxxx" detail="discordbot_channel"></div>
</label>
</div>
</div>
</div>
<% } %>
<% if(details.use_ldap!=='0'){ %>
<div class="form-group-group forestgreen">
<h4><%-lang.LDAP%></h4>

View File

@ -10,13 +10,40 @@
</div>
<div class="modal-body" style="max-height:600px;overflow:auto">
<div class="text-center msg"></div>
<div class="form-group">
<label><div><span><%-lang['All Monitors and Privileges']%></span></div>
<div><select class="form-control" detail="allmonitors">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
<div class="form-group-group">
<h4><%-lang['Privileges']%></h4>
<div class="form-group">
<label><div><span><%-lang['All Monitors and Privileges']%></span></div>
<div><select class="form-control" detail="allmonitors">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group permission-view">
<label><div><span><%-lang['Can Create and Delete Monitors']%></span></div>
<div><select class="form-control" detail="monitor_create">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group permission-view">
<label><div><span><%-lang['Can Change User Settings']%></span></div>
<div><select class="form-control" detail="user_change">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
<div class="form-group">
<label><div><span><%-lang['Can View Logs']%></span></div>
<div><select class="form-control" detail="view_logs">
<option value="0" selected><%-lang.No%></option>
<option value="1"><%-lang.Yes%></option>
</select></div>
</label>
</div>
</div>
<div class="form-group-group blue permission-view" id="monitors_section">
<h4>

View File

@ -31,7 +31,8 @@
<div class="modal-footer">
<div class="row">
<div class="col-md-4 text-left">
<a class="btn btn-danger delete_selected"><i class="fa fa-trash-o"></i> &nbsp; <%-lang['Delete selected']%></a>
<a class="btn btn-danger delete_selected"><i class="fa fa-trash-o"></i> &nbsp; <%-lang['Delete']%></a>
<a class="btn btn-default export_selected"><i class="fa fa-folder-o"></i> &nbsp; <%-lang['Zip and Download']%></a>
</div>
<div class="col-md-4">
<div class="text-center" id="videos_viewer_pages"></div>

View File

@ -107,9 +107,10 @@ if(data.addon.indexOf('gui')>-1){ %>
<% };
if(data.addon.indexOf('fullscreen')>-1){ %>
<style>
body,html{overflow: hidden;}
body,html{overflow: hidden;height:100%}
*{margin:0;padding:0;border:0}
.stream-element,.shinobi_stream{position:absolute;top:0;left:0;}
.stream-element,.shinobi_stream{position:absolute;top:0;left:0;height:100%}
.shinobi_stream video{object-fit: fill}
</style>
<script>
$(window).resize(function(){

View File

@ -19,21 +19,12 @@
<%= details.css %>
</style>
<style>
<% if(details.sub&&details.allmonitors==='0'){
if(details.monitor_edit&&details.monitor_edit!==''){
details.monitor_edit.forEach(function(v,n){ %>
[mid="<%= v %>"] .permission_monitor_edit{display:inline-block}
<%
})
}
if(details.video_delete&&details.video_delete!==''){
details.video_delete.forEach(function(v,n){ %>
[mid="<%= v %>"] .permission_video_delete{display:inline-block}
<%
})
}
<% if(details.video_delete&&details.video_delete!==''){
details.video_delete.forEach(function(v,n){ %>
[mid="<%= v %>"] .permission_video_delete{display:inline-block}
<%
})
}else{ %>
.permission_video_delete,.permission_monitor_edit{display:inline-block}
th.permission_video_delete,td.permission_video_delete{display:table-cell}
<% } %>
</style>
@ -50,7 +41,7 @@
<div class="mdl-layout__header-row">
<ul class="nav navbar-nav">
<li title="<%-lang['Toggle Sidebar']%>" class_toggle="hide-side" data-target=".mdl-js-layout"><a>&nbsp;<i class="fa fa-bars"></i>&nbsp;</a></li>
<li title="<%-lang['Add Monitor']%>" mid="" ke="" class="hidden-xs permission_monitor_edit"><a monitor="edit">&nbsp;<i class="fa fa-plus"></i>&nbsp;</a></li>
<li title="<%-lang['Add Monitor']%>" mid="" ke="" class="hidden-xs permission_monitor_create"><a monitor="edit">&nbsp;<i class="fa fa-plus"></i>&nbsp;</a></li>
<li title="<%-lang['Power Video Viewer']%>" class="hidden-xs" mid="" ke=""><a monitor="powerview">&nbsp;<i class="fa fa-map-marker"></i>&nbsp;</a></li>
<li>
<a title="<%-lang['Monitor Groups']%>" id="group_list_button" class="mdl-js-button">&nbsp;<i class="fa fa-video-camera"></i>&nbsp;</a>
@ -122,18 +113,19 @@
<li class="mdl-menu__item" data-toggle="modal" data-target="#multi_mon"><div><i class="fa fa-clone"></i><div><%- lang['Monitors'] %></div></div></li>
<li class="mdl-menu__item" mid="" ke=""><div class="flex" monitor="powerview"><i class="fa fa-map-marker"></i><div><%- lang['Power Viewer'] %></div></div></li>
<li class="mdl-menu__item" mid="" ke=""><div class="flex" monitor="timelapse"><i class="fa fa-angle-double-right"></i><div><%- lang['Time-lapse'] %></div></div></li>
<li class="mdl-menu__item" data-toggle="modal" data-target="#settings"><div><i class="fa fa-gears"></i><div><%- lang.Settings %></div></div></li>
<li class="mdl-menu__item permission_user_change" data-toggle="modal" data-target="#settings"><div><i class="fa fa-gears"></i><div><%- lang.Settings %></div></div></li>
<li class="mdl-menu__item" data-toggle="modal" data-target="#apis"><div><i class="fa fa-code"></i><div><%- lang.API %></div></div></li>
<% if(!details.sub){ %>
<li class="mdl-menu__item" data-toggle="modal" data-target="#onvif_probe"><div><i class="fa fa-rss"></i><div><%- lang.ONVIF %></div></div></li>
<li class="mdl-menu__item" data-toggle="modal" data-target="#probe"><div><i class="fa fa-search"></i><div><%- lang.FFprobe %></div></div></li>
<li class="mdl-menu__item" data-toggle="modal" data-target="#filters"><div><i class="fa fa-filter"></i><div><%- lang.Filters %></div></div></li>
<% } %>
<li class="mdl-menu__item" data-toggle="modal" data-target="#logs_modal"><div><i class="fa fa-exclamation-triangle"></i><div><%- lang.Logs %></div></div></li>
<li class="mdl-menu__item permission_view_logs" data-toggle="modal" data-target="#logs_modal"><div><i class="fa fa-exclamation-triangle"></i><div><%- lang.Logs %></div></div></li>
<li class="mdl-menu__item" class_toggle="list-blocks" data-target="#left_menu"><div><i class="fa fa-camera"></i><div><%- lang['List Toggle'] %></div></div></li>
<li class="mdl-menu__item" class_toggle="hide-side" data-target=".mdl-js-layout"><div><i class="fa fa-bars"></i><div><%- lang['Hide List'] %></div></div></li>
<li class="mdl-menu__item shinobi-detector-motion shinobi-detector-opencv shinobi-detector_plug" class_toggle="hide_indifference" data-target="body" style="display:none"><div><i class="fa fa-bolt"></i><div><%- lang['Motion GUI'] %></div></div></li>
<li class="mdl-menu__item" system="jpegToggle"><div><i class="fa fa-file-image-o"></i><div><%- lang['JPEG Mode'] %></div></div></li>
<li class="mdl-menu__item" system="switch" switch="monitorMuteAudio" type="text"><div><i class="fa fa-volume-down"></i><div><%- lang['Mute Audio'] %></div></div></li>
<li class="mdl-menu__item" system="switch" switch="monitorOrder" type="text"><div><i class="fa fa-sort"></i><div><%- lang['Order Streams'] %></div></div></li>
<li class="mdl-menu__item" system="switch" switch="notifyHide" type="text"><div><i class="fa fa-exclamation-circle"></i><div><%- lang['Hide Notes'] %></div></div></li>
<li class="mdl-menu__item logout"><div><i class="fa fa-sign-out"></i><div><%- lang.Logout %></div></div></li>

View File

@ -17,7 +17,6 @@ requires https or firefox
</div>
<video id="video"><source></video>
<canvas id="canvas"></canvas>
<script src="libs/js/jquery.min.js"></script>
<script src="libs/js/socket.io.js"></script>
<script src="libs/js/menu.js"></script>
<script>