diff --git a/COPYING b/COPYING.md
similarity index 100%
rename from COPYING
rename to COPYING.md
diff --git a/LICENSE b/LICENSE.md
similarity index 100%
rename from LICENSE
rename to LICENSE.md
diff --git a/camera.js b/camera.js
index 51802f6..3ce8f87 100644
--- a/camera.js
+++ b/camera.js
@@ -120,6 +120,7 @@ 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.iconURL === undefined){config.iconURL = "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png"}
if(config.pipeAddition === undefined){config.pipeAddition=7}else{config.pipeAddition=parseInt(config.pipeAddition)}
//Child Nodes
if(config.childNodes === undefined)config.childNodes = {};
@@ -271,7 +272,7 @@ if(config.discordBot === true){
fields: [],
timestamp: new Date(),
footer: {
- icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png",
+ icon_url: config.iconURL,
text: "Shinobi Systems"
}
},data)
@@ -329,9 +330,11 @@ s.ocvTx=function(data){
//send data to socket client function
s.tx = function(z,y,x){if(x){return x.broadcast.to(y).emit('f',z)};io.to(y).emit('f',z);}
s.txToDashcamUsers = function(data,groupKey){
- Object.keys(s.group[groupKey].dashcamUsers).forEach(function(auth){
- s.tx(data,s.group[groupKey].dashcamUsers[auth].cnid)
- })
+ if(s.group[groupKey] && s.group[groupKey].dashcamUsers){
+ Object.keys(s.group[groupKey].dashcamUsers).forEach(function(auth){
+ s.tx(data,s.group[groupKey].dashcamUsers[auth].cnid)
+ })
+ }
}
s.txWithSubPermissions = function(z,y,permissionChoices){
if(typeof permissionChoices==='string'){
@@ -518,7 +521,7 @@ s.createPamDiffEngine = function(e){
imgWidth:width
}
detectorObject.doObjectDetection = (s.ocv && e.details.detector_use_detect_object === '1')
- s.camera('motion',detectorObject)
+ s.event('trigger',detectorObject)
if(detectorObject.doObjectDetection === true){
s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:s.group[e.ke].mon[e.id].lastJpegDetectorFrame});
}
@@ -627,6 +630,7 @@ s.kill = function(x,e,p){
delete(s.group[e.ke].mon[e.id].spawn_exit);
}catch(er){}
}
+ s.group[e.ke].mon[e.id].firstStreamChunk = {}
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].checkStream);
@@ -680,9 +684,9 @@ s.systemLog = function(q,w,e){
}
//system log
s.debugLog = function(q,w,e){
- if(!w){w = ''}
- if(!e){e = ''}
if(config.debugLog === true){
+ if(!w){w = ''}
+ if(!e){e = ''}
console.log(s.timeObject().format(),q,w,e)
if(config.debugLogVerbose === true){
console.log(new Error())
@@ -975,14 +979,14 @@ s.init=function(x,e,k,fn){
// s.discordMsg({
// author: {
// name: s.group[e.ke].mon_conf[e.id].name,
-// icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png"
+// icon_url: config.iconURL
// },
// title: lang['Status Changed'],
// description: lang['Monitor is now '+e.status],
// fields: [],
// timestamp: new Date(),
// footer: {
-// icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png",
+// icon_url: config.iconURL,
// text: "Shinobi Systems"
// }
// },[],e.ke)
@@ -1323,7 +1327,7 @@ s.video=function(x,e,k){
k.stat = fs.statSync(k.dir+k.file)
e.filesize = k.stat.size
e.filesizeMB = parseFloat((e.filesize/1000000).toFixed(2))
-
+
e.startTime = new Date(s.nameToTime(k.file))
e.endTime = new Date(k.stat.mtime)
if(config.useUTC === true){
@@ -1415,9 +1419,9 @@ s.video=function(x,e,k){
console.error(err)
})
s.group[e.ke].aws_s3.upload({
- Bucket: s.group[e.ke].init.aws_s3_bucket,
- Key: s.group[e.ke].init.aws_s3_dir+e.ke+'/'+e.mid+'/'+k.filename,
- Body:fileStream,
+ Bucket: s.group[e.ke].init.aws_s3_bucket,
+ Key: s.group[e.ke].init.aws_s3_dir+e.ke+'/'+e.mid+'/'+k.filename,
+ Body:fileStream,
ACL:'public-read'
},function(err,data){
if(err){
@@ -1501,7 +1505,7 @@ s.createInputMap = function(e,number,input){
//input - probe size
if(input.probesize&&input.probesize!==''){x.cust_input+=' -probesize '+input.probesize}
//input - stream loop (good for static files/lists)
- if(input.stream_loop==='1'){x.cust_input+=' -stream_loop -1'}
+ if(input.stream_loop === '1'){x.cust_input+=' -stream_loop -1'}
//input - fps
if(x.cust_input.indexOf('-r ')===-1&&input.sfps&&input.sfps!==''){
input.sfps=parseFloat(input.sfps);
@@ -1599,7 +1603,7 @@ s.createStreamChannel = function(e,number,channel){
//stream - custom flags
if(channel.cust_stream&&channel.cust_stream!==''){x.cust_stream=' '+channel.cust_stream}else{x.cust_stream=''}
//stream - preset
- if(channel.preset_stream&&channel.preset_stream!==''){x.preset_stream=' -preset '+channel.preset_stream;}else{x.preset_stream=''}
+ if(channel.stream_type !== 'h265' && channel.preset_stream && channel.preset_stream!==''){x.preset_stream=' -preset '+channel.preset_stream;}else{x.preset_stream=''}
//hardware acceleration
if(e.details.accelerator&&e.details.accelerator==='1'){
if(e.details.hwaccel&&e.details.hwaccel!==''){
@@ -1646,8 +1650,8 @@ s.createStreamChannel = function(e,number,channel){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices['stream_channel-'+(number-config.pipeAddition)])
}
- if(channel.stream_vcodec!=='copy'){
- x.cust_stream+=x.stream_fps
+ if(channel.stream_vcodec !== 'copy' || channel.stream_type === 'mjpeg' || channel.stream_type === 'b64'){
+ x.cust_stream += x.stream_fps
}
switch(channel.stream_type){
case'mp4':
@@ -1730,7 +1734,7 @@ s.ffmpegCoProcessor = function(e){
//x.input is the input and connection
if(e.details.loglevel&&e.details.loglevel!==''){x.loglevel='-loglevel '+e.details.loglevel;}else{x.loglevel='-loglevel error'}
x.input = x.loglevel+' -re -i '+e.sdir+'cpuOnly.m3u8'
-
+
//x.pipe is the stream out methods
x.cust_input=''
x.cust_detect=' '
@@ -1787,8 +1791,8 @@ s.ffmpegCoProcessor = function(e){
}
if(e.details.cust_stream&&e.details.cust_stream!==''){x.cust_stream=' '+e.details.cust_stream}else{x.cust_stream=''}
if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps=''}
- if(e.details.stream_vcodec!=='copy'){
- x.cust_stream+=x.stream_fps
+ if(e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64'){
+ x.cust_stream += x.stream_fps
}
switch(e.details.stream_type){
case'mjpeg':
@@ -1804,18 +1808,35 @@ s.ffmpegCoProcessor = function(e){
}
//detector frames
if(e.details.detector === '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.detector_fps && e.details.detector_fps !== ''){
+ x.detector_fps = e.details.detector_fps
+ }else{
+ x.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;}
if(e.details.detector_pam==='1'){
- x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe -r '+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3'
+ x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe -r '+x.detector_fps+x.cust_detect+x.dratio+' pipe:3'
if(e.details.detector_use_detect_object === '1'){
+ if(e.details.detector_use_motion === '1'){
+ if(e.details.detector_scale_x_object && e.details.detector_scale_x_object !== '' && e.details.detector_scale_y_object && e.details.detector_scale_y_object !== ''){
+ x.dratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object
+ }
+ if(e.details.detector_fps_object && e.details.detector_fps_object !== ''){
+ x.detector_fps = e.details.detector_fps_object
+ }
+ }
//for object detection
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
- x.pipe += ' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:4';
+ x.pipe += ' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:4';
}
}else{
- x.pipe+=' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3';
+ x.pipe+=' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:3';
}
}
//snapshot frames
@@ -1901,7 +1922,14 @@ s.coSpawnClose = function(e){
s.ffmpeg = function(e){
e.coProcessor = false
e.isStreamer = (e.type === 'dashcam'|| e.type === 'socket')
- if(e.details.accelerator === '1' && e.details.hwaccel_vcodec !== 'auto' && e.isStreamer === false && (!e.details.input_maps || e.details.input_maps.length === 0)){
+ if(
+ e.details.accelerator === '1' &&
+ e.details.hwaccel !== 'vaapi' &&
+ e.details.hwaccel_vcodec !== 'auto' &&
+ e.isStreamer === false &&
+ (!e.details.input_maps || e.details.input_maps.length === 0) &&
+ (e.details.snap === '1' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64' || e.details.detector === '1')
+ ){
e.coProcessor = true
}
//set X for temporary values so we don't break our main monitor object.
@@ -1921,7 +1949,7 @@ s.ffmpeg = function(e){
//input - probe size
if(e.details.probesize&&e.details.probesize!==''){x.cust_input+=' -probesize '+e.details.probesize};
//input - stream loop (good for static files/lists)
- if(e.details.stream_loop==='1'){x.cust_input+=' -stream_loop -1'};
+ if(e.details.stream_loop === '1' && (e.type === 'mp4' || e.type === 'local')){x.cust_input+=' -stream_loop -1'};
//input
if(e.details.cust_input.indexOf('-fflags') === -1){x.cust_input+=' -fflags +igndts'}
switch(e.type){
@@ -2084,7 +2112,7 @@ s.ffmpeg = function(e){
//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=''}
+ if(e.details.stream_type !== 'h265' && e.details.preset_stream && e.details.preset_stream !== ''){x.preset_stream=' -preset '+e.details.preset_stream;}else{x.preset_stream=''}
//stream - quality
//hardware acceleration
if(e.details.accelerator && e.details.accelerator==='1' && e.isStreamer === false){
@@ -2131,8 +2159,8 @@ s.ffmpeg = function(e){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.stream)
}
- if(e.details.stream_vcodec!=='copy'){
- x.cust_stream+=x.stream_fps
+ if(e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64'){
+ x.cust_stream += x.stream_fps
}
switch(e.details.stream_type){
case'mp4':
@@ -2171,12 +2199,15 @@ s.ffmpeg = function(e){
x.pipe+=' -an -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+' pipe:1';
}
break;
- case'pam':
- if(e.coProcessor === false){
+ case'h265':
+ x.cust_stream+=' -movflags +frag_keyframe+empty_moov+default_base_moof -metadata title="Shinobi H.265 Stream" -reset_timestamps 1'
+ if(e.details.stream_vcodec!=='copy'){
if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions}
- if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality;
- x.pipe+=' -an -c:v pam -pix_fmt rgba -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1';
+ if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -crf '+e.details.stream_quality;
+ x.cust_stream+=x.preset_stream
+ x.cust_stream+=x.stream_video_filters
}
+ x.pipe+=' -f hevc'+x.stream_acodec+x.stream_vcodec+x.cust_stream+' pipe:1';
break;
case'b64':case'':case undefined:case null://base64
if(e.coProcessor === false){
@@ -2191,29 +2222,28 @@ s.ffmpeg = function(e){
}
if(e.details.stream_channels){
e.details.stream_channels.forEach(function(v,n){
+ if(v.stream_type === 'mjpeg')e.coProcessor = true;
x.pipe += s.createStreamChannel(e,n+config.pipeAddition,v)
})
}
//detector - plugins, motion
- if(e.details.detector==='1'&&e.details.detector_send_frames==='1'){
- if(e.details.accelerator !== '1'){
- if(e.details.input_map_choices&&e.details.input_map_choices.detector){
- //add input feed map
+ if(e.details.detector==='1' && e.details.detector_send_frames==='1' && e.coProcessor === false){
+ if(e.details.input_map_choices&&e.details.input_map_choices.detector){
+ //add input feed map
+ x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
+ }
+ 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;}
+ if(e.details.detector_pam==='1'){
+ x.pipe+=' -an -c:v pam -pix_fmt gray -f image2pipe -r '+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3'
+ if(e.details.detector_use_detect_object === '1'){
+ //for object detection
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
+ x.pipe += ' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:4';
}
- 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;}
- if(e.details.detector_pam==='1'){
- x.pipe+=' -an -c:v pam -pix_fmt gray -f image2pipe -r '+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3'
- if(e.details.detector_use_detect_object === '1'){
- //for object detection
- x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
- x.pipe += ' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:4';
- }
- }else{
- x.pipe+=' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3';
- }
+ }else{
+ x.pipe+=' -f singlejpeg -vf fps='+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3';
}
}
//api - snapshot bin/ cgi.bin (JPEG Mode)
@@ -2327,7 +2357,7 @@ s.ffmpeg = function(e){
//create executeable FFMPEG command
x.ffmpegCommandString = x.loglevel+x.input_fps;
//progress pipe
-// x.ffmpegCommandString += ' -progress pipe:5';
+ x.ffmpegCommandString += ' -progress pipe:5';
//add main input
if((e.type === 'mp4' || e.type === 'mjpeg') && x.cust_input.indexOf('-re') === -1){
x.cust_input += ' -re'
@@ -2391,6 +2421,438 @@ s.file=function(x,e){
break;
}
}
+s.event = function(x,e,cn){
+ switch(x){
+ case'trigger':
+ var d=e;
+ var filter = {
+ halt : false,
+ addToMotionCounter : true,
+ useLock : true,
+ save : true,
+ discord : true,
+ webhook : true,
+ command : true,
+ mail : true,
+ record : true
+ }
+ if(s.group[d.ke].mon[d.id].open){
+ d.details.videoTime = s.group[d.ke].mon[d.id].open;
+ }
+ var detailString = JSON.stringify(d.details);
+ if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){
+ return s.systemLog(lang['No Monitor Found, Ignoring Request'])
+ }
+ d.mon=s.group[d.ke].mon_conf[d.id];
+ var currentConfig = s.group[d.ke].mon[d.id].details
+ //read filters
+ if(
+ currentConfig.use_detector_filters === '1' &&
+ ((currentConfig.use_detector_filters_object === '1' && d.details.matrices) ||
+ currentConfig.use_detector_filters_object !== '1')
+ ){
+ var parseValue = function(key,val){
+ var newVal
+ switch(val){
+ case'':
+ newVal = filter[key]
+ break;
+ case'0':
+ newVal = false
+ break;
+ case'1':
+ newVal = true
+ break;
+ default:
+ newVal = val
+ break;
+ }
+ return newVal
+ }
+ var filters = currentConfig.detector_filters
+ Object.keys(filters).forEach(function(key){
+ var conditionChain = {}
+ var dFilter = filters[key]
+ dFilter.where.forEach(function(condition,place){
+ conditionChain[place] = {ok:false,next:condition.p4,matrixCount:0}
+ if(d.details.matrices)conditionChain[place].matrixCount = d.details.matrices.length
+ var modifyFilters = function(toCheck,matrixPosition){
+ var param = toCheck[condition.p1]
+ var pass = function(){
+ if(matrixPosition && dFilter.actions.halt === '1'){
+ delete(d.details.matrices[matrixPosition])
+ }else{
+ conditionChain[place].ok = true
+ }
+ }
+ switch(condition.p2){
+ case'indexOf':
+ if(param.indexOf(condition.p3) > -1){
+ pass()
+ }
+ break;
+ case'!indexOf':
+ if(param.indexOf(condition.p3) === -1){
+ pass()
+ }
+ break;
+ default:
+ var cmd = 'param '+condition.p2+' "'+condition.p3.replace(/"/g,'\\"')+'"'
+ if(eval('param '+condition.p2+' "'+condition.p3.replace(/"/g,'\\"')+'"')){
+ pass()
+ }
+ break;
+ }
+ }
+ switch(condition.p1){
+ case'tag':
+ case'x':
+ case'y':
+ case'height':
+ case'width':
+ if(d.details.matrices){
+ d.details.matrices.forEach(function(matrix,position){
+ modifyFilters(matrix,position)
+ })
+ }
+ break;
+ default:
+ modifyFilters(d.details)
+ break;
+ }
+ })
+ var conditionArray = Object.values(conditionChain)
+ var validationString = ''
+ conditionArray.forEach(function(condition,number){
+ validationString += condition.ok+' '
+ if(conditionArray.length-1 !== number){
+ validationString += condition.next+' '
+ }
+ })
+ if(eval(validationString)){
+ if(dFilter.actions.halt !== '1'){
+ delete(dFilter.actions.halt)
+ Object.keys(dFilter.actions).forEach(function(key){
+ var value = dFilter.actions[key]
+ filter[key] = parseValue(key,value)
+ })
+ }else{
+ filter.halt = true
+ }
+ }
+ })
+ if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){
+ return
+ }else if(d.details.matrices && d.details.matrices.length > 0){
+ var reviewedMatrix = []
+ d.details.matrices.forEach(function(matrix){
+ if(matrix)reviewedMatrix.push(matrix)
+ })
+ d.details.matrices = reviewedMatrix
+ }
+ }
+ //motion counter
+ if(filter.addToMotionCounter && filter.record){
+ if(!s.group[d.ke].mon[d.id].detector_motion_count){
+ s.group[d.ke].mon[d.id].detector_motion_count=0
+ }
+ s.group[d.ke].mon[d.id].detector_motion_count+=1
+ }
+ if(filter.useLock){
+ if(s.group[d.ke].mon[d.id].motion_lock){
+ return
+ }
+ var detector_lock_timeout
+ if(!currentConfig.detector_lock_timeout||currentConfig.detector_lock_timeout===''){
+ detector_lock_timeout = 2000
+ }
+ detector_lock_timeout = parseFloat(currentConfig.detector_lock_timeout);
+ if(!s.group[d.ke].mon[d.id].detector_lock_timeout){
+ s.group[d.ke].mon[d.id].detector_lock_timeout=setTimeout(function(){
+ clearTimeout(s.group[d.ke].mon[d.id].detector_lock_timeout)
+ delete(s.group[d.ke].mon[d.id].detector_lock_timeout)
+ },detector_lock_timeout)
+ }else{
+ return
+ }
+ }
+ if(d.doObjectDetection !== true){
+ //save this detection result in SQL, only coords. not image.
+ if(filter.save && currentConfig.detector_save==='1'){
+ s.sqlQuery('INSERT INTO Events (ke,mid,details) VALUES (?,?,?)',[d.ke,d.id,detailString])
+ }
+ if(currentConfig.detector_notrigger=='1'){
+ var detector_notrigger_timeout
+ if(!currentConfig.detector_notrigger_timeout||currentConfig.detector_notrigger_timeout===''){
+ detector_notrigger_timeout = 10
+ }
+ detector_notrigger_timeout = parseFloat(currentConfig.detector_notrigger_timeout)*1000*60;
+ s.group[e.ke].mon[e.id].detector_notrigger_timeout = detector_notrigger_timeout;
+ clearInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout)
+ s.group[d.ke].mon[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
+ }
+ if(filter.webhook && currentConfig.detector_webhook=='1'){
+ var detector_webhook_url = currentConfig.detector_webhook_url
+ .replace(/{{TIME}}/g,s.timeObject(new Date).format())
+ .replace(/{{REGION_NAME}}/g,d.details.name)
+ .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg')
+ .replace(/{{MONITOR_ID}}/g,d.id)
+ .replace(/{{GROUP_KEY}}/g,d.ke)
+ .replace(/{{DETAILS}}/g,detailString)
+ request({url:detector_webhook_url,method:'GET',encoding:null},function(err,data){
+ if(err){
+ s.log(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}})
+ }
+ })
+ }
+ var detector_timeout
+ if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
+ detector_timeout = 10
+ }else{
+ detector_timeout = parseFloat(currentConfig.detector_timeout)
+ }
+ if(filter.record && d.mon.mode=='start'&¤tConfig.detector_trigger==='1'&¤tConfig.detector_record_method==='sip'){
+ //s.group[d.ke].mon[d.id].eventBasedRecording.timeout
+ // clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
+ s.group[d.ke].mon[d.id].eventBasedRecording.timeout = setTimeout(function(){
+ s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd=true;
+ },detector_timeout * 950 * 60)
+ if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){
+ if(!d.auth){
+ d.auth=s.gid();
+ }
+ if(!s.group[d.ke].users[d.auth]){
+ s.group[d.ke].users[d.auth]={system:1,details:{},lang:lang}
+ }
+ s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false;
+ var runRecord = function(){
+ var filename = s.formattedTime()+'.mp4'
+ s.log(d,{type:"Traditional Recording",msg:"Started"})
+ //-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
+ s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i http://'+config.ip+':'+config.port+'/'+d.auth+'/hls/'+d.ke+'/'+d.id+'/detectorStream.m3u8 -t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+' -c:v copy -strftime 1 "'+s.video('getDir',d.mon) + filename + '"').replace(/\s+/g,' ').trim()))
+ var ffmpegError='';
+ var error
+ s.group[d.ke].mon[d.id].eventBasedRecording.process.stderr.on('data',function(data){
+ s.log(d,{type:"Traditional Recording",msg:data.toString()})
+ })
+ s.group[d.ke].mon[d.id].eventBasedRecording.process.on('close',function(){
+ if(!s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd){
+ s.log(d,{type:"Traditional Recording",msg:"Detector Recording Process Exited Prematurely. Restarting."})
+ runRecord()
+ return
+ }
+ s.video('insertCompleted',d.mon,{
+ file : filename
+ })
+ s.log(d,{type:"Traditional Recording",msg:"Detector Recording Complete"})
+ delete(s.group[d.ke].users[d.auth])
+ s.log(d,{type:"Traditional Recording",msg:'Clear Recorder Process'})
+ delete(s.group[d.ke].mon[d.id].eventBasedRecording.process)
+ delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
+ clearTimeout(s.group[d.ke].mon[d.id].checker)
+ })
+ }
+ runRecord()
+ }
+ }else if(filter.record && d.mon.mode!=='stop'&¤tConfig.detector_trigger=='1'&¤tConfig.detector_record_method==='hot'){
+ if(!d.auth){
+ d.auth=s.gid();
+ }
+ if(!s.group[d.ke].users[d.auth]){
+ s.group[d.ke].users[d.auth]={system:1,details:{},lang:lang}
+ }
+ d.urlQuery=[]
+ d.url='http://'+config.ip+':'+config.port+'/'+d.auth+'/monitor/'+d.ke+'/'+d.id+'/record/'+detector_timeout+'/min';
+ if(currentConfig.watchdog_reset!=='0'){
+ d.urlQuery.push('reset=1')
+ }
+ if(currentConfig.detector_trigger_record_fps&¤tConfig.detector_trigger_record_fps!==''&¤tConfig.detector_trigger_record_fps!=='0'){
+ d.urlQuery.push('fps='+currentConfig.detector_trigger_record_fps)
+ }
+ if(d.urlQuery.length>0){
+ d.url+='?'+d.urlQuery.join('&')
+ }
+ http.get(d.url, function(data) {
+ data.setEncoding('utf8');
+ var chunks='';
+ data.on('data', (chunk) => {
+ chunks+=chunk;
+ });
+ data.on('end', () => {
+ delete(s.group[d.ke].users[d.auth])
+ d.cx.f='detector_record_engaged';
+ d.cx.msg=JSON.parse(chunks);
+ s.tx(d.cx,'GRP_'+d.ke);
+ });
+
+ }).on('error', function(e) {
+
+ }).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(filter.discord && currentConfig.detector_discordbot === '1' && !s.group[d.ke].mon[d.id].detector_discordbot){
+ var detector_discordbot_timeout
+ if(!currentConfig.detector_discordbot_timeout||currentConfig.detector_discordbot_timeout===''){
+ detector_discordbot_timeout = 1000*60*10;
+ }else{
+ detector_discordbot_timeout = parseFloat(currentConfig.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.discordMsg({
+ author: {
+ name: s.group[d.ke].mon_conf[d.id].name,
+ icon_url: config.iconURL
+ },
+ title: lang.Event+' - '+screenshotName,
+ description: lang.EventText1+' '+s.timeObject(new Date).format(),
+ fields: [],
+ timestamp: new Date(),
+ footer: {
+ icon_url: config.iconURL,
+ text: "Shinobi Systems"
+ }
+ },files,d.ke)
+ }
+ if(currentConfig.detector_discordbot_send_video === '1'){
+ 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(currentConfig.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(filter.mail && config.mail && !s.group[d.ke].mon[d.id].detector_mail && currentConfig.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
+ if(!currentConfig.detector_mail_timeout||currentConfig.detector_mail_timeout===''){
+ detector_mail_timeout = 1000*60*10;
+ }else{
+ detector_mail_timeout = parseFloat(currentConfig.detector_mail_timeout)*1000*60;
+ }
+ //lock mailer so you don't get emailed on EVERY trigger event.
+ s.group[d.ke].mon[d.id].detector_mail=setTimeout(function(){
+ //unlock so you can mail again.
+ clearTimeout(s.group[d.ke].mon[d.id].detector_mail);
+ delete(s.group[d.ke].mon[d.id].detector_mail);
+ },detector_mail_timeout);
+ var files = []
+ var mailOptions = {
+ from: config.mail.from, // sender address
+ to: r.mail, // list of receivers
+ subject: lang.Event+' - '+screenshotName, // Subject line
+ html: ''+lang.EventText1+' '+s.timeObject(new Date).format()+'. ',
+ attachments: files
+ }
+ var sendMail = function(){
+ Object.keys(d.details).forEach(function(v,n){
+ mailOptions.html+='
'+v+' : '+d.details[v]+'
'
+ })
+ nodemailer.sendMail(mailOptions, (error, info) => {
+ if (error) {
+ s.systemLog(lang.MailError,error)
+ return false;
+ }
+ })
+ }
+ if(currentConfig.detector_mail_send_video === '1'){
+ if(!detectorStreamBuffers){
+ detectorStreamBuffers = s.getDetectorStreams(d)
+ }
+ detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){
+ files.push({
+ filename: 'Video Clip '+n+'.ts',
+ content: fs.readFileSync(filepath)
+ })
+ })
+ }
+ if(screenshotBuffer){
+ sendMail()
+ }else if(currentConfig.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
+ })
+ }
+ sendMail()
+ })
+ }else{
+ sendMail()
+ }
+ });
+ }
+ if(filter.command && currentConfig.detector_command_enable==='1'&&!s.group[d.ke].mon[d.id].detector_command){
+ var detector_command_timeout
+ if(!currentConfig.detector_command_timeout||currentConfig.detector_command_timeout===''){
+ detector_command_timeout = 1000*60*10;
+ }else{
+ detector_command_timeout = parseFloat(currentConfig.detector_command_timeout)*1000*60;
+ }
+ s.group[d.ke].mon[d.id].detector_command=setTimeout(function(){
+ clearTimeout(s.group[d.ke].mon[d.id].detector_command);
+ delete(s.group[d.ke].mon[d.id].detector_command);
+
+ },detector_command_timeout);
+ var detector_command = currentConfig.detector_command
+ .replace(/{{TIME}}/g,s.timeObject(new Date).format())
+ .replace(/{{REGION_NAME}}/g,d.details.name)
+ .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg')
+ .replace(/{{MONITOR_ID}}/g,d.id)
+ .replace(/{{GROUP_KEY}}/g,d.ke)
+ .replace(/{{DETAILS}}/g,detailString)
+ if(d.details.confidence){
+ detector_command = detector_command
+ .replace(/{{CONFIDENCE}}/g,d.details.confidence)
+ }
+ exec(detector_command,{detached: true})
+ }
+ }
+ //show client machines the event
+ d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
+ s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
+ break;
+ }
+ if(typeof cn==='function'){setTimeout(function(){cn()},1000);}
+}
s.camera=function(x,e,cn,tx){
if(x!=='motion'){
var ee=s.init('noReference',e);
@@ -2402,14 +2864,15 @@ s.camera=function(x,e,cn,tx){
try{e.details=JSON.parse(e.details)}catch(err){}
}
//parse Objects
- (['detector_cascades','cords','input_map_choices']).forEach(function(v){
+ (['detector_cascades','cords','detector_filters','input_map_choices']).forEach(function(v){
if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){
try{
if(e.details[v] === '') e.details[v] = '{}'
e.details[v]=JSON.parse(e.details[v]);
if(!e.details[v])e.details[v]={};
+ s.group[e.ke].mon[e.id].details = e.details;
}catch(err){
-
+
}
}
});
@@ -3063,6 +3526,15 @@ s.camera=function(x,e,cn,tx){
if(!s.group[e.ke].mon[e.id].record){s.group[e.ke].mon[e.id].record={yes:1}};
//launch ffmpeg (main)
s.group[e.ke].mon[e.id].spawn = s.ffmpeg(e)
+ s.group[e.ke].mon[e.id].spawn.stdio[5].on('data',function(data){
+ resetStreamCheck()
+ // var progress = {}
+ // data.toString().split('\n').forEach(function(v){
+ // var split = v.split('=')
+ // var val = split[1]
+ // if(val)progress[split[0]] = val
+ // })
+ })
if(e.type === 'dashcam'){
setTimeout(function(){
s.group[e.ke].mon[e.id].allowStdinWrite = true
@@ -3078,7 +3550,7 @@ s.camera=function(x,e,cn,tx){
s.group[e.ke].mon[e.id].spawn_exit=function(){
if(s.group[e.ke].mon[e.id].started===1){
if(e.details.loglevel!=='quiet'){
- s.log(e,{type:lang['Process Unexpected Exit'],msg:{msg:lang['Process Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].mon[e.id].ffmpeg}});
+ s.log(e,{type:lang['Process Unexpected Exit'],msg:{msg:lang['Process Crashed for Monitor'],cmd:s.group[e.ke].mon[e.id].ffmpeg}});
}
errorFatal('Process Unexpected Exit');
}
@@ -3089,7 +3561,7 @@ s.camera=function(x,e,cn,tx){
//emitter for mjpeg
if(!e.details.stream_mjpeg_clients||e.details.stream_mjpeg_clients===''||isNaN(e.details.stream_mjpeg_clients)===false){e.details.stream_mjpeg_clients=20;}else{e.details.stream_mjpeg_clients=parseInt(e.details.stream_mjpeg_clients)}
s.group[e.ke].mon[e.id].emitter = new events.EventEmitter().setMaxListeners(e.details.stream_mjpeg_clients);
- s.log(e,{type:'FFMPEG Process Started',msg:{cmd:s.group[e.ke].mon[e.id].ffmpeg}});
+ s.log(e,{type:lang['Process Started'],msg:{cmd:s.group[e.ke].mon[e.id].ffmpeg}});
s.tx({f:'monitor_starting',mode:x,mid:e.id,time:s.formattedTime()},'GRP_'+e.ke);
//start workers
if(e.type==='jpeg'){
@@ -3215,6 +3687,12 @@ s.camera=function(x,e,cn,tx){
s.group[e.ke].mon[e.id].emitter.emit('data',d);
}
break;
+ case'h265':
+ e.frame_to_stream=function(d){
+ resetStreamCheck()
+ s.group[e.ke].mon[e.id].emitter.emit('data',d);
+ }
+ break;
// case'pam':
// s.group[e.ke].mon[e.id].p2pStream = new P2P();
// s.group[e.ke].mon[e.id].spawn.stdout.pipe(s.group[e.ke].mon[e.id].p2pStream)
@@ -3245,7 +3723,7 @@ s.camera=function(x,e,cn,tx){
}
if(e.frame_to_stream){
if(e.coProcessor === true && e.details.stream_type === ('b64'||'mjpeg')){
-
+
}else{
s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frame_to_stream)
}
@@ -3447,318 +3925,6 @@ s.camera=function(x,e,cn,tx){
launchMonitorProcesses();
}
break;
- case'motion':
- var d=e;
- if(s.group[d.ke].mon[d.id].open){
- d.details.videoTime = s.group[d.ke].mon[d.id].open;
- }
- var detailString = JSON.stringify(d.details);
- if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){
- return s.systemLog(lang['No Monitor Found, Ignoring Request'])
- }
- d.mon=s.group[d.ke].mon_conf[d.id];
- if(!s.group[d.ke].mon[d.id].detector_motion_count){
- s.group[d.ke].mon[d.id].detector_motion_count=0
- }
- s.group[d.ke].mon[d.id].detector_motion_count+=1
- if(s.group[d.ke].mon[d.id].motion_lock){
- return
- }
- var detector_lock_timeout
- if(!d.mon.details.detector_lock_timeout||d.mon.details.detector_lock_timeout===''){
- detector_lock_timeout = 2000
- }
- detector_lock_timeout = parseFloat(d.mon.details.detector_lock_timeout);
- if(!s.group[d.ke].mon[d.id].detector_lock_timeout){
- s.group[d.ke].mon[d.id].detector_lock_timeout=setTimeout(function(){
- clearTimeout(s.group[d.ke].mon[d.id].detector_lock_timeout)
- delete(s.group[d.ke].mon[d.id].detector_lock_timeout)
- },detector_lock_timeout)
- }else{
- return
- }
- if(d.doObjectDetection !== true){
- //save this detection result in SQL, only coords. not image.
- if(d.mon.details.detector_save==='1'){
- s.sqlQuery('INSERT INTO Events (ke,mid,details) VALUES (?,?,?)',[d.ke,d.id,detailString])
- }
- if(d.mon.details.detector_notrigger=='1'){
- var detector_notrigger_timeout
- if(!d.mon.details.detector_notrigger_timeout||d.mon.details.detector_notrigger_timeout===''){
- detector_notrigger_timeout = 10
- }
- detector_notrigger_timeout = parseFloat(d.mon.details.detector_notrigger_timeout)*1000*60;
- s.group[e.ke].mon[e.id].detector_notrigger_timeout = detector_notrigger_timeout;
- clearInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout)
- s.group[d.ke].mon[d.id].detector_notrigger_timeout = setInterval(s.group[d.ke].mon[d.id].detector_notrigger_timeout_function,detector_notrigger_timeout)
- }
- if(d.mon.details.detector_webhook=='1'){
- var detector_webhook_url = d.mon.details.detector_webhook_url
- .replace(/{{TIME}}/g,s.timeObject(new Date).format())
- .replace(/{{REGION_NAME}}/g,d.details.name)
- .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg')
- .replace(/{{MONITOR_ID}}/g,d.id)
- .replace(/{{GROUP_KEY}}/g,d.ke)
- .replace(/{{DETAILS}}/g,detailString)
- http.get(detector_webhook_url, function(data) {
- data.setEncoding('utf8');
- var chunks='';
- data.on('data', (chunk) => {
- chunks+=chunk;
- });
- data.on('end', () => {
-
- });
-
- }).on('error', function(e) {
-
- }).end();
- }
- var detector_timeout
- if(!d.mon.details.detector_timeout||d.mon.details.detector_timeout===''){
- detector_timeout = 10
- }else{
- detector_timeout = parseFloat(d.mon.details.detector_timeout)
- }
- if(d.mon.mode=='start'&&d.mon.details.detector_trigger==='1'&&d.mon.details.detector_record_method==='sip'){
- //s.group[d.ke].mon[d.id].eventBasedRecording.timeout
- // clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
- s.group[d.ke].mon[d.id].eventBasedRecording.timeout = setTimeout(function(){
- s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd=true;
- },detector_timeout * 950 * 60)
- if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){
- if(!d.auth){
- d.auth=s.gid();
- }
- if(!s.group[d.ke].users[d.auth]){
- s.group[d.ke].users[d.auth]={system:1,details:{},lang:lang}
- }
- s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false;
- var runRecord = function(){
- var filename = s.formattedTime()+'.mp4'
- s.log(d,{type:"Traditional Recording",msg:"Started"})
- //-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
- s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i http://'+config.ip+':'+config.port+'/'+d.auth+'/hls/'+d.ke+'/'+d.id+'/detectorStream.m3u8 -t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+' -c:v copy -strftime 1 "'+s.video('getDir',d.mon) + filename + '"').replace(/\s+/g,' ').trim()))
- var ffmpegError='';
- var error
- s.group[d.ke].mon[d.id].eventBasedRecording.process.stderr.on('data',function(data){
- s.log(d,{type:"Traditional Recording",msg:data.toString()})
- })
- s.group[d.ke].mon[d.id].eventBasedRecording.process.on('close',function(){
- if(!s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd){
- s.log(d,{type:"Traditional Recording",msg:"Detector Recording Process Exited Prematurely. Restarting."})
- runRecord()
- return
- }
- s.video('insertCompleted',d.mon,{
- file : filename
- })
- s.log(d,{type:"Traditional Recording",msg:"Detector Recording Complete"})
- delete(s.group[d.ke].users[d.auth])
- s.log(d,{type:"Traditional Recording",msg:'Clear Recorder Process'})
- delete(s.group[d.ke].mon[d.id].eventBasedRecording.process)
- delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
- clearTimeout(s.group[d.ke].mon[d.id].checker)
- })
- }
- runRecord()
- }
- }else if(d.mon.mode!=='stop'&&d.mon.details.detector_trigger=='1'&&d.mon.details.detector_record_method==='hot'){
- if(!d.auth){
- d.auth=s.gid();
- }
- if(!s.group[d.ke].users[d.auth]){
- s.group[d.ke].users[d.auth]={system:1,details:{},lang:lang}
- }
- d.urlQuery=[]
- d.url='http://'+config.ip+':'+config.port+'/'+d.auth+'/monitor/'+d.ke+'/'+d.id+'/record/'+detector_timeout+'/min';
- if(d.mon.details.watchdog_reset!=='0'){
- d.urlQuery.push('reset=1')
- }
- if(d.mon.details.detector_trigger_record_fps&&d.mon.details.detector_trigger_record_fps!==''&&d.mon.details.detector_trigger_record_fps!=='0'){
- d.urlQuery.push('fps='+d.mon.details.detector_trigger_record_fps)
- }
- if(d.urlQuery.length>0){
- d.url+='?'+d.urlQuery.join('&')
- }
- http.get(d.url, function(data) {
- data.setEncoding('utf8');
- var chunks='';
- data.on('data', (chunk) => {
- chunks+=chunk;
- });
- data.on('end', () => {
- delete(s.group[d.ke].users[d.auth])
- d.cx.f='detector_record_engaged';
- d.cx.msg=JSON.parse(chunks);
- s.tx(d.cx,'GRP_'+d.ke);
- });
-
- }).on('error', function(e) {
-
- }).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.discordMsg({
- 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(d.mon.details.detector_discordbot_send_video === '1'){
- 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'){
- 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
- if(!d.mon.details.detector_mail_timeout||d.mon.details.detector_mail_timeout===''){
- detector_mail_timeout = 1000*60*10;
- }else{
- detector_mail_timeout = parseFloat(d.mon.details.detector_mail_timeout)*1000*60;
- }
- //lock mailer so you don't get emailed on EVERY trigger event.
- s.group[d.ke].mon[d.id].detector_mail=setTimeout(function(){
- //unlock so you can mail again.
- clearTimeout(s.group[d.ke].mon[d.id].detector_mail);
- delete(s.group[d.ke].mon[d.id].detector_mail);
- },detector_mail_timeout);
- var files = []
- var mailOptions = {
- from: config.mail.from, // sender address
- to: r.mail, // list of receivers
- subject: lang.Event+' - '+screenshotName, // Subject line
- html: ''+lang.EventText1+' '+s.timeObject(new Date).format()+'. ',
- attachments: files
- }
- var sendMail = function(){
- Object.keys(d.details).forEach(function(v,n){
- mailOptions.html+=''+v+' : '+d.details[v]+'
'
- })
- nodemailer.sendMail(mailOptions, (error, info) => {
- if (error) {
- s.systemLog(lang.MailError,error)
- return false;
- }
- })
- }
- if(d.mon.details.detector_mail_send_video === '1'){
- if(!detectorStreamBuffers){
- detectorStreamBuffers = s.getDetectorStreams(d)
- }
- detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){
- files.push({
- filename: 'Video Clip '+n+'.ts',
- content: fs.readFileSync(filepath)
- })
- })
- }
- 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
- })
- }
- sendMail()
- })
- }else{
- sendMail()
- }
- });
- }
- if(d.mon.details.detector_command_enable==='1'&&!s.group[d.ke].mon[d.id].detector_command){
- var detector_command_timeout
- if(!d.mon.details.detector_command_timeout||d.mon.details.detector_command_timeout===''){
- detector_command_timeout = 1000*60*10;
- }else{
- detector_command_timeout = parseFloat(d.mon.details.detector_command_timeout)*1000*60;
- }
- s.group[d.ke].mon[d.id].detector_command=setTimeout(function(){
- clearTimeout(s.group[d.ke].mon[d.id].detector_command);
- delete(s.group[d.ke].mon[d.id].detector_command);
-
- },detector_command_timeout);
- var detector_command = d.mon.details.detector_command
- .replace(/{{TIME}}/g,s.timeObject(new Date).format())
- .replace(/{{REGION_NAME}}/g,d.details.name)
- .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg')
- .replace(/{{MONITOR_ID}}/g,d.id)
- .replace(/{{GROUP_KEY}}/g,d.ke)
- .replace(/{{DETAILS}}/g,detailString)
- if(d.details.confidence){
- detector_command = detector_command
- .replace(/{{CONFIDENCE}}/g,d.details.confidence)
- }
- exec(detector_command,{detached: true})
- }
- }
- //show client machines the event
- d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
- s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
- break;
}
if(typeof cn==='function'){setTimeout(function(){cn()},1000);}
}
@@ -3767,7 +3933,7 @@ s.camera=function(x,e,cn,tx){
s.pluginEventController=function(d){
switch(d.f){
case'trigger':
- s.camera('motion',d)
+ s.event('trigger',d)
break;
case's.tx':
s.tx(d.data,d.to)
@@ -3812,7 +3978,6 @@ s.pluginInitiatorSuccess=function(mode,d,cn){
s.api[d.plug]={pluginEngine:d.plug,permissions:{},details:{},ip:'0.0.0.0'};
}
s.pluginInitiatorFail=function(mode,d,cn){
- s.connectedPlugins[d.plug].plugged=false
if(mode==='client'){
//is in client mode (camera.js is client)
cn.disconnect()
@@ -3886,6 +4051,79 @@ var tx;
}
})
//unique Base64 socket stream
+ cn.on('h265',function(d){
+ if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
+ cn.disconnect();return;
+ }
+ cn.ip=cn.request.connection.remoteAddress;
+ var toUTC = function(){
+ return new Date().toISOString();
+ }
+ var tx=function(z){cn.emit('data',z);}
+ d.failed=function(msg){
+ tx({f:'stop_reconnect',msg:msg,token_used:d.auth,ke:d.ke});
+ cn.disconnect();
+ }
+ d.success=function(r){
+ r=r[0];
+ var Emitter,chunkChannel
+ if(!d.channel){
+ Emitter = s.group[d.ke].mon[d.id].emitter
+ chunkChannel = 'MAIN'
+ }else{
+ Emitter = s.group[d.ke].mon[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition]
+ chunkChannel = parseInt(d.channel)+config.pipeAddition
+ }
+ if(!Emitter){
+ cn.disconnect();return;
+ }
+ if(!d.channel)d.channel = 'MAIN';
+ cn.ke=d.ke,
+ cn.uid=d.uid,
+ cn.auth=d.auth;
+ cn.channel=d.channel;
+ cn.removeListenerOnDisconnect=true;
+ cn.socketVideoStream=d.id;
+ var contentWriter
+ cn.closeSocketVideoStream = function(){
+ Emitter.removeListener('data', contentWriter);
+ }
+ Emitter.on('data',contentWriter = function(base64){
+ tx(base64)
+ })
+ }
+ //check if auth key is user's temporary session key
+ if(s.group[d.ke]&&s.group[d.ke].users&&s.group[d.ke].users[d.auth]){
+ d.success(s.group[d.ke].users[d.auth]);
+ }else{
+ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) {
+ if(r&&r[0]){
+ d.success(r)
+ }else{
+ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) {
+ if(r&&r[0]){
+ r=r[0]
+ r.details=JSON.parse(r.details)
+ if(r.details.auth_socket==='1'){
+ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) {
+ if(r&&r[0]){
+ d.success(r)
+ }else{
+ d.failed('User not found')
+ }
+ })
+ }else{
+ d.failed('Permissions for this key do not allow authentication with Websocket')
+ }
+ }else{
+ d.failed('Not an API key')
+ }
+ })
+ }
+ })
+ }
+ })
+ //unique Base64 socket stream
cn.on('Base64',function(d){
if(!s.group[d.ke]||!s.group[d.ke].mon||!s.group[d.ke].mon[d.id]){
cn.disconnect();return;
@@ -5033,42 +5271,42 @@ var tx;
break;
}
})
- //functions for retrieving cron announcements
- cn.on('cron',function(d){
- if(d.f==='init'){
- if(config.cron.key){
- if(config.cron.key===d.cronKey){
- s.cron={started:moment(),last_run:moment(),id:cn.id};
- }else{
- cn.disconnect()
- }
- }else{
- s.cron={started:moment(),last_run:moment(),id:cn.id};
- }
- }else{
- if(s.cron&&cn.id===s.cron.id){
- delete(d.cronKey)
- switch(d.f){
+ //functions for retrieving cron announcements
+ cn.on('cron',function(d){
+ if(d.f==='init'){
+ if(config.cron.key){
+ if(config.cron.key===d.cronKey){
+ s.cron={started:moment(),last_run:moment(),id:cn.id};
+ }else{
+ cn.disconnect()
+ }
+ }else{
+ s.cron={started:moment(),last_run:moment(),id:cn.id};
+ }
+ }else{
+ if(s.cron&&cn.id===s.cron.id){
+ delete(d.cronKey)
+ switch(d.f){
case'filters':
- s.filterEvents(d.ff,d);
- break;
- case's.tx':
- s.tx(d.data,d.to)
- break;
- case's.video':
- s.video(d.data,d.file)
- break;
- case'start':case'end':
- d.mid='_cron';s.log(d,{type:'cron',msg:d.msg})
- break;
- default:
- s.systemLog('CRON : ',d)
- break;
- }
- }else{
- cn.disconnect()
- }
- }
+ s.filterEvents(d.ff,d);
+ break;
+ case's.tx':
+ s.tx(d.data,d.to)
+ break;
+ case's.video':
+ s.video(d.data,d.file)
+ break;
+ case'start':case'end':
+ d.mid='_cron';s.log(d,{type:'cron',msg:d.msg})
+ break;
+ default:
+ s.systemLog('CRON : ',d)
+ break;
+ }
+ }else{
+ cn.disconnect()
+ }
+ }
})
cn.on('disconnect', function () {
if(cn.socketVideoStream){
@@ -5097,8 +5335,8 @@ var tx;
s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU')
delete(s.api[cn.pluginEngine])
}
- if(cn.cron){
- delete(s.cron);
+ if(cn.cron){
+ delete(s.cron);
}
if(cn.ocv){
s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU')
@@ -5296,6 +5534,8 @@ if(config.renderPaths === undefined){config.renderPaths={}}
if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
//gridstack only page
if(config.renderPaths.grid === undefined){config.renderPaths.grid='pages/grid'}
+ //slick.js (cycle) page
+ if(config.renderPaths.cycle === undefined){config.renderPaths.cycle='pages/cycle'}
////Pages
app.enable('trust proxy');
app.use('/libs',express.static(__dirname + '/web/libs'));
@@ -5337,7 +5577,7 @@ app.get(config.webPaths.admin, function (req,res){
});
//super page
app.get(config.webPaths.super, function (req,res){
-
+
res.render(config.renderPaths.index,{lang:lang,config:config,screen:'super',originalURL:s.getOriginalUrl(req)},function(err,html){
if(err){
s.systemLog(err)
@@ -5524,7 +5764,8 @@ app.post([config.webPaths.home,s.checkCorrectPathEnding(config.webPaths.home)+':
}
if(!r.details.acceptedMachines[req.body.machineID]){
req.complete=function(){
- s.factorAuth[r.ke][r.uid].info=req.resp;
+ s.factorAuth[r.ke][r.uid].function = req.body.function
+ s.factorAuth[r.ke][r.uid].info = req.resp
clearTimeout(s.factorAuth[r.ke][r.uid].expireAuth)
s.factorAuth[r.ke][r.uid].expireAuth=setTimeout(function(){
s.deleteFactorAuth(r)
@@ -5534,20 +5775,37 @@ app.post([config.webPaths.home,s.checkCorrectPathEnding(config.webPaths.home)+':
if(!s.factorAuth[r.ke]){s.factorAuth[r.ke]={}}
if(!s.factorAuth[r.ke][r.uid]){
s.factorAuth[r.ke][r.uid]={key:s.nid(),user:r}
- r.mailOptions = {
- from: config.mail.from,
- to: r.mail,
- subject: r.lang['2-Factor Authentication'],
- html: r.lang['Enter this code to proceed']+' '+s.factorAuth[r.ke][r.uid].key+' . '+r.lang.FactorAuthText1,
- };
- nodemailer.sendMail(r.mailOptions, (error, info) => {
- if (error) {
- s.systemLog(r.lang.MailError,error)
- req.fn(r)
- return
- }
- req.complete()
- });
+ if(r.details.factor_mail !== '0'){
+ var mailOptions = {
+ from: config.mail.from,
+ to: r.mail,
+ subject: r.lang['2-Factor Authentication'],
+ html: r.lang['Enter this code to proceed']+' '+s.factorAuth[r.ke][r.uid].key+' . '+r.lang.FactorAuthText1,
+ };
+ nodemailer.sendMail(mailOptions, (error, info) => {
+ if (error) {
+ s.systemLog(r.lang.MailError,error)
+ return
+ }
+ });
+ }
+ if(r.details.factor_discord === '1'){
+ s.discordMsg({
+ author: {
+ name: r.lang['2-Factor Authentication'],
+ icon_url: config.iconURL
+ },
+ title: r.lang['Enter this code to proceed'],
+ description: '**'+s.factorAuth[r.ke][r.uid].key+'** '+r.lang.FactorAuthText1,
+ fields: [],
+ timestamp: new Date(),
+ footer: {
+ icon_url: config.iconURL,
+ text: "Shinobi Systems"
+ }
+ },[],r.ke)
+ }
+ req.complete()
}else{
req.complete()
}
@@ -5719,7 +5977,8 @@ app.post([config.webPaths.home,s.checkCorrectPathEnding(config.webPaths.home)+':
s.sqlQuery("UPDATE Users SET details=? WHERE ke=? AND uid=?",[s.s(req.details),req.body.ke,req.body.id])
}
}
- req.resp=s.factorAuth[req.body.ke][req.body.id].info
+ req.body.function = s.factorAuth[req.body.ke][req.body.id].function
+ req.resp = s.factorAuth[req.body.ke][req.body.id].info
req.fn(s.factorAuth[req.body.ke][req.body.id].user)
}else{
req.renderFunction(config.renderPaths.factorAuth,{$user:s.factorAuth[req.body.ke][req.body.id].info,lang:req.lang});
@@ -5814,15 +6073,49 @@ app.get([config.webPaths.apiPrefix+':auth/flv/:ke/:id/s.flv',config.webPaths.api
},res,req)
},res,req)
})
+//Get H.265/h265 HEVC stream
+app.get([config.webPaths.apiPrefix+':auth/h265/:ke/:id/s.hevc',config.webPaths.apiPrefix+':auth/h265/:ke/:id/:channel/s.hevc'], function(req,res) {
+ res.header("Access-Control-Allow-Origin",req.headers.origin);
+ s.auth(req.params,function(user){
+ s.checkChildProxy(req.params,function(){
+ var Emitter,chunkChannel
+ if(!req.params.channel){
+ Emitter = s.group[req.params.ke].mon[req.params.id].emitter
+ chunkChannel = 'MAIN'
+ }else{
+ Emitter = s.group[req.params.ke].mon[req.params.id].emitterChannel[parseInt(req.params.channel)+config.pipeAddition]
+ chunkChannel = parseInt(req.params.channel)+config.pipeAddition
+ }
+ //variable name of contentWriter
+ var contentWriter
+ //set headers
+ res.setHeader('Content-Type', 'video/mp4');
+ res.setHeader('Access-Control-Allow-Origin','*');
+ //write new frames as they happen
+ Emitter.on('data',contentWriter=function(buffer){
+ res.write(buffer)
+ })
+ //remove contentWriter when client leaves
+ res.on('close', function () {
+ Emitter.removeListener('data',contentWriter)
+ })
+ },res,req)
+ },res,req)
+})
//montage - stand alone squished view with gridstackjs
-app.get([config.webPaths.apiPrefix+':auth/grid/:ke',config.webPaths.apiPrefix+':auth/grid/:ke/:group'], function(req,res) {
+app.get([
+ config.webPaths.apiPrefix+':auth/grid/:ke',
+ config.webPaths.apiPrefix+':auth/grid/:ke/:group',
+ config.webPaths.apiPrefix+':auth/cycle/:ke',
+ config.webPaths.apiPrefix+':auth/cycle/:ke/:group'
+], function(req,res) {
res.header("Access-Control-Allow-Origin",req.headers.origin);
s.auth(req.params,function(user){
if(user.permissions.get_monitors==="0"){
res.end(user.lang['Not Permitted'])
return
}
-
+
req.params.protocol=req.protocol;
req.sql='SELECT * FROM Monitors WHERE mode!=? AND mode!=? AND ke=?';req.ar=['stop','idle',req.params.ke];
if(!req.params.id){
@@ -5857,7 +6150,7 @@ app.get([config.webPaths.apiPrefix+':auth/grid/:ke',config.webPaths.apiPrefix+':
}
})
}catch(err){
-
+
}
})
r = filteredByGroup;
@@ -5901,14 +6194,19 @@ app.get([config.webPaths.apiPrefix+':auth/grid/:ke',config.webPaths.apiPrefix+':
}
}
})
- res.render(config.renderPaths.grid,{
+ var page = config.renderPaths.grid
+ if(req.path.indexOf('/cycle/') > -1){
+ page = config.renderPaths.cycle
+ }
+ res.render(page,{
data:Object.assign(req.params,req.query),
baseUrl:req.protocol+'://'+req.hostname,
config:config,
lang:user.lang,
$user:user,
monitors:r,
- originalURL:s.getOriginalUrl(req)
+ originalURL:s.getOriginalUrl(req),
+ query:req.query
});
})
},res,req)
@@ -6918,7 +7216,7 @@ app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){
res.end(user.lang['No Group with this key exists']);
return;
}
- s.camera('motion',d,function(){
+ s.event('trigger',d,function(){
res.end(user.lang['Trigger Successful'])
});
},res,req);
@@ -7441,7 +7739,7 @@ if(config.childNodes.enabled === true && config.childNodes.mode === 'master'){
})
})
}else
-//setup Child for childNodes
+//setup Child for childNodes
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
s.connected = false;
childIO = require('socket.io-client')('ws://'+config.childNodes.host);
diff --git a/conf.sample.json b/conf.sample.json
index e24865c..e2530fa 100644
--- a/conf.sample.json
+++ b/conf.sample.json
@@ -23,6 +23,7 @@
"pluginKeys":{
"Motion":"change_this_to_something_very_random____make_sure_to_match__/plugins/motion/conf.json",
"OpenCV":"change_this_to_something_very_random____make_sure_to_match__/plugins/opencv/conf.json",
- "OpenALPR":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi"
+ "OpenALPR":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi",
+ "PythonYolo":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi"
}
}
\ No newline at end of file
diff --git a/definitions/en_CA.json b/definitions/en_CA.json
index 37b9494..aa76b27 100644
--- a/definitions/en_CA.json
+++ b/definitions/en_CA.json
@@ -248,11 +248,15 @@
"info": [
{
"name": "detail=stream_type",
- "field": "Stream Type",
+ "field": "Stream Type",
"description": "The method that will used to consume the video stream.",
"default": "HLS",
"example": "",
"possible": [
+ {
+ "name": "Poseidon",
+ "info": "Simulates a streaming MP4 file but using the data of a live stream. Includes Audio. Some browsers can play it like a regular MP4 file. Streams over HTTP or WebSocket."
+ },
{
"name": "MJPEG",
"info": "Standard Motion JPEG image. No audio."
diff --git a/languages/en_CA.json b/languages/en_CA.json
index 2815577..f8bef10 100644
--- a/languages/en_CA.json
+++ b/languages/en_CA.json
@@ -114,6 +114,10 @@
"Saved Filters": "Saved Filters",
"Filter Name": "Filter Name",
"Find Where": "Find Where",
+ "Reason": "Reason",
+ "Detection Engine": "Detection Engine",
+ "X Point": "X Point",
+ "Y Point": "Y Point",
"Sort By": "Sort By",
"Start Time": "Start Time",
"End Time": "End Time",
@@ -128,6 +132,10 @@
"Greater Than": "Greater Than",
"Less Than": "Less Than",
"Less Than or Equal to": "Less Than or Equal to",
+ "Contains": "Contains",
+ "Does Not Contain": "Does Not Contain",
+ "AND": "AND",
+ "OR": "OR",
"Like": "Like",
"Matches": "Matches",
"Not Matches": "Not Matches",
@@ -293,6 +301,8 @@
"Recording Timestamp": "Recording Timestamp",
"Recording Watermark": "Recording Watermark",
"Region Editor": "Region Editor",
+ "Detector Filters": "Detector Filters",
+ "Filter for Objects only": "Filter for Objects only",
"Custom": "Custom",
"Detector": "Detector",
"Connected": "Connected",
@@ -380,6 +390,7 @@
"Recording Flags": "Recording Flags",
"Output Method": "Output Method",
"Webhook": "Webhook",
+ "Event Webhook Error": "Event Webhook Error",
"Webhook URL": "Webhook URL",
"Command on Trigger": "Command on Trigger",
"Command": "Command",
@@ -388,6 +399,7 @@
"Save Events to SQL": "Save Events to SQL",
"Email on Trigger": "Email on Trigger Emails go to the main account holder's login address. ",
"Attach Video Clip": "Attach Video Clip",
+ "Discord": "Discord",
"Discord Alert on Trigger": "Discord Alert on Trigger",
"Allow Next Email": "Allow Next Email in Minutes ",
"Allow Next Discord Alert": "Allow Next Discord Alert in Minutes ",
@@ -459,6 +471,7 @@
"Audio": "Audio",
"Mute Audio": "Mute Audio",
"No Audio": "No Audio",
+ "Popout Monitor on Event": "Popout Monitor on Event",
"aac": "aac",
"ac3": "ac3",
"libmp3lame": "libmp3lame",
@@ -473,6 +486,7 @@
"Bottom Left": "Bottom Left",
"WebM (libvpx)": "WebM (libvpx)",
"Poseidon": "Poseidon",
+ "HEVC (H.265)": "HEVC (H.265)",
"MP4 (copy, libx264, libx265)": "MP4 (copy, libx264, libx265)",
"Default": "Default",
"libvpx (Default)": "libvpx (Default)",
@@ -517,8 +531,10 @@
"No Videos Found": "No Videos Found",
"FileNotExistText": "Cannot save non existant file. Something went wrong.",
"CameraNotRecordingText": "Settings may be incompatible. Check encoders. Restarting...",
+ "Camera is not running": "Camera is not running",
"Camera is not recording": "Camera is not recording",
"Camera is not streaming": "Camera is not streaming",
+ "Process Started": "Process Started",
"Restarting Process": "Restarting Process",
"Restarting": "Restarting",
"Starting": "Starting",
@@ -660,6 +676,10 @@
"hevc_qsv": "H.265 (Quick Sync Video)",
"vp8_qsv": "VP8 (Quick Sync Video)",
"mpeg2_qsv": "MPEG2 (Quick Sync Video)",
+ "h264_mmal": "H.264 (Raspberry Pi)",
+ "mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
+ "mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
+ "h264_omx": "H.264 openMAX (Raspberry Pi)",
"h264_vaapi": "H.264 VA-API (Intel HW Accel)",
"h264_nvenc": "H.264 NVENC (NVIDIA HW Accel)",
"hevc_nvenc": "H.265 NVENC (NVIDIA HW Accel)",
diff --git a/languages/fr.json b/languages/fr.json
index 41b0358..f8ce5eb 100644
--- a/languages/fr.json
+++ b/languages/fr.json
@@ -610,6 +610,10 @@
"h264_nvenc": "H. 264 NVENC (NVIDIA HW Accel)",
"h264_qsv": "H. 264 (Quick Sync Video)",
"h264_vaapi": "H. 264 VA-API (Intel HW Accel)",
+ "h264_mmal": "H.264 (Raspberry Pi)",
+ "mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
+ "mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
+ "h264_omx": "H.264 openMAX (Raspberry Pi)",
"hevc_cuvid": "H. 265 CUVID",
"hevc_nvenc": "H. 265 NVENC (NVIDIA HW Accel)",
"hevc_qsv": "H. 265 (Quick Sync Video)",
diff --git a/web/libs/css/main.dash2.css b/web/libs/css/main.dash2.css
index d210341..b0daee1 100644
--- a/web/libs/css/main.dash2.css
+++ b/web/libs/css/main.dash2.css
@@ -287,6 +287,7 @@ form.modal-body{margin:0}
.dropdown-menu.scrollable{max-height:300px}
.upload_file input{display:none}
#video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto}
+.stream-block,.stream-objects{overflow: hidden!important}
.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1}
.stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;}
.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px}
diff --git a/web/libs/js/main.dash2.js b/web/libs/js/main.dash2.js
index e50738e..485875f 100644
--- a/web/libs/js/main.dash2.js
+++ b/web/libs/js/main.dash2.js
@@ -383,6 +383,9 @@ switch($user.details.lang){
case'flv':
streamURL=$.ccio.init('location',user)+user.auth_token+'/flv/'+d.ke+'/'+d.mid+'/s.flv'
break;
+ case'h265':
+ streamURL=$.ccio.init('location',user)+user.auth_token+'/h265/'+d.ke+'/'+d.mid+'/s.hevc'
+ break;
case'mp4':
streamURL=$.ccio.init('location',user)+user.auth_token+'/mp4/'+d.ke+'/'+d.mid+'/s.mp4'
break;
@@ -514,7 +517,7 @@ switch($user.details.lang){
$.ccio.mon_groups[b][v.mid]=v;
})
}catch(er){
-
+
}
})
return $.ccio.mon_groups;
@@ -655,7 +658,7 @@ switch($user.details.lang){
d.fn=function(){
if(!d.speed){d.speed=1000}
switch(d.d.stream_type){
- case'b64':case'pam':
+ case'b64':case'h265':
d.p.resize()
break;
case'hls':case'flv':case'mp4':
@@ -770,8 +773,10 @@ switch($user.details.lang){
ctx.drawImage(img, 0, 0,c.width,c.height);
extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height)
break;
- case'pam':
- alert('Need to add')
+ case'h265':
+ var c = $('[mid='+e.mon.mid+'].monitor_item canvas')[0];
+ var ctx = c.getContext('2d');
+ extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height)
break;
case'b64':
base64 = e.mon.last_frame.split(',')[1];
@@ -779,7 +784,7 @@ switch($user.details.lang){
image_data.src = base64;
extend(atob(base64),image_data.width,image_data.height)
break;
- case'jpeg':
+ case'jpeg':case'h265':
url=e.p.find('.stream-element').attr('src');
image_data = new Image();
image_data.src = url;
@@ -1129,7 +1134,7 @@ switch($user.details.lang){
case'jpeg':
tmp+=' ';
break;
- default://base64
+ default://base64//h265
tmp+=' ';
break;
}
@@ -1153,6 +1158,63 @@ switch($user.details.lang){
}
$.ccio.init('ls')
break;
+ case'detector-filters-where':
+ if(!d)d={};
+ d.id=$('#filters_where .row').length;
+ if(!d.p1){d.p1='indifference'}
+ if(!d.p2){d.p2='='}
+ if(!d.p3){d.p3=''}
+ if(!d.p4){d.p4='&&'}
+ tmp+=''
+ tmp+='
'
+ tmp+=' '
+ tmp+=' '
+ tmp+=' <%-cleanLang(lang['Indifference'])%> '
+ tmp+=' <%-cleanLang(lang['Region Name'])%> '
+ tmp+=' <%-cleanLang(lang['Reason'])%> '
+ tmp+=' <%-cleanLang(lang['Detection Engine'])%> '
+ tmp+=' '
+ tmp+=' <%-cleanLang(lang['Object Tag'])%> '
+ tmp+=' <%-cleanLang(lang['Confidence'])%> '
+ tmp+=' <%-cleanLang(lang['X Point'])%> '
+ tmp+=' <%-cleanLang(lang['Y Point'])%> '
+ tmp+=' <%-cleanLang(lang['Height'])%> '
+ tmp+=' <%-cleanLang(lang['Width'])%> '
+ tmp+=' '
+ tmp+=' '
+ tmp+=' '
+ tmp+='
'
+ tmp+='
'
+ tmp+=' '
+ tmp+=' '
+ tmp+=' <%-cleanLang(lang['Equal to'])%> '
+ tmp+=' <%-cleanLang(lang['Not Equal to'])%> '
+ tmp+=' <%-cleanLang(lang['Contains'])%> '
+ tmp+=' <%-cleanLang(lang['Does Not Contain'])%> '
+ tmp+=' '
+ tmp+=' <%-cleanLang(lang['Greater Than or Equal to'])%> '
+ tmp+=' <%-cleanLang(lang['Greater Than'])%> '
+ tmp+=' <%-cleanLang(lang['Less Than'])%> '
+ tmp+=' <%-cleanLang(lang['Less Than or Equal to'])%> '
+ tmp+=' '
+ tmp+=' '
+ tmp+=' '
+ tmp+='
'
+ tmp+='
'
+ tmp+=' '
+ tmp+=' '
+ tmp+=' '
+ tmp+='
'
+ tmp+='
'
+ tmp+=' '
+ tmp+=' '
+ tmp+=' <%-cleanLang(lang['AND'])%> '
+ tmp+=' <%-cleanLang(lang['OR'])%> '
+ tmp+=' '
+ tmp+=' '
+ tmp+='
'
+ tmp+='
'
+ break;
case'filters-where':
if(!d)d={};
d.id=$('#filters_where .row').length;
@@ -1382,9 +1444,12 @@ switch($user.details.lang){
{label:'<%-cleanLang(lang['hevc_cuvid'])%>',value:'hevc_cuvid',group:'NVIDIA'},
{label:'<%-cleanLang(lang['mjpeg_cuvid'])%>',value:'mjpeg_cuvid',group:'NVIDIA'},
{label:'<%-cleanLang(lang['mpeg4_cuvid'])%>',value:'mpeg4_cuvid',group:'NVIDIA'},
- {label:'<%-cleanLang(lang['h264_qsv'])%>',value:'h264_qsv',group:'QuickSync Video'},
- {label:'<%-cleanLang(lang['hevc_qsv'])%>',value:'hevc_qsv',group:'QuickSync Video'},
- {label:'<%-cleanLang(lang['mpeg2_qsv'])%>',value:'mpeg2_qsv',group:'QuickSync Video'},
+ {label:'<%-cleanLang(lang['h264_qsv'])%>',value:'h264_qsv',group:'Quick Sync Video'},
+ {label:'<%-cleanLang(lang['hevc_qsv'])%>',value:'hevc_qsv',group:'Quick Sync Video'},
+ {label:'<%-cleanLang(lang['mpeg2_qsv'])%>',value:'mpeg2_qsv',group:'Quick Sync Video'},
+ {label:'<%-cleanLang(lang['h264_mmal'])%>',value:'h264_mmal',group:'Raspberry Pi'},
+ {label:'<%-cleanLang(lang['mpeg2_mmal'])%>',value:'mpeg2_mmal',group:'Raspberry Pi'},
+ {label:'<%-cleanLang(lang['mpeg4_mmal'])%>',value:'mpeg4_mmal',group:'Raspberry Pi'},
]
},
{
@@ -1494,6 +1559,7 @@ switch($user.details.lang){
tmp+=' <%-lang["h264_qsv"]%> '
tmp+=' <%-lang["hevc_qsv"]%> '
tmp+=' <%-lang["mpeg2_qsv"]%> '
+ tmp+=' <%-lang["h264_omx"]%> '
tmp+=' '
tmp+=' '
tmp+=' '
@@ -1654,6 +1720,14 @@ switch($user.details.lang){
k.mon=$.ccio.mon[d.ke+d.mid+user.auth_token]
$.ccio.init('monitorInfo',k)
break;
+ case'detector-filters-where':
+ $('#detector_filters_where').append(tmp);
+ $('#detector_filters_where .row [where="p4"][disabled]').prop('disabled',false)
+ $('#detector_filters_where .row:last [where="p1"]').val(d.p1)
+ $('#detector_filters_where .row:last [where="p2"]').val(d.p2)
+ $('#detector_filters_where .row:last [where="p3"]').val(d.p3)
+ $('#detector_filters_where .row:last [where="p4"]').val(d.p4).prop('disabled',true)
+ break;
case'filters-where':
$('#filters_where').append(tmp);
$('#filters_where .row:last [where="p1"]').val(d.p1)
@@ -1831,6 +1905,9 @@ $.ccio.globalWebsocket=function(d,user){
},user.details.audio_delay * 1000)
}
}
+ if(user.details.event_mon_pop === '1' && (!$.ccio.mon[d.ke+d.id+user.auth_token].popOut || $.ccio.mon[d.ke+d.id+user.auth_token].popOut.closed === true)){
+ d.e.find('[monitor="pop"]').click()
+ }
}
break;
case'init_success':
@@ -1972,7 +2049,14 @@ $.ccio.globalWebsocket=function(d,user){
clearInterval($.ccio.mon[d.ke+d.id+user.auth_token].signal);delete($.ccio.mon[d.ke+d.id+user.auth_token].signal);
$.ccio.mon[d.ke+d.id+user.auth_token].watch=0;
if($.ccio.mon[d.ke+d.id+user.auth_token].hls){$.ccio.mon[d.ke+d.id+user.auth_token].hls.destroy()}
+ if($.ccio.mon[d.ke+d.id+user.auth_token].Poseidon){$.ccio.mon[d.ke+d.id+user.auth_token].Poseidon.destroy()}
+ if($.ccio.mon[d.ke+d.id+user.auth_token].Base64){$.ccio.mon[d.ke+d.id+user.auth_token].Base64.disconnect()}
+ if($.ccio.mon[d.ke+d.id+user.auth_token].h265Socket){$.ccio.mon[d.ke+d.id+user.auth_token].h265Socket.disconnect()}
+ if($.ccio.mon[d.ke+d.id+user.auth_token].h265Player){$.ccio.mon[d.ke+d.id+user.auth_token].h265Player.stop()}
if($.ccio.mon[d.ke+d.id+user.auth_token].dash){$.ccio.mon[d.ke+d.id+user.auth_token].dash.reset()}
+ if($.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream && $.ccio.mon[d.ke+d.id+user.auth_token].abort){
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream.abort()
+ }
$.grid.data().removeWidget($('#monitor_live_'+d.id+user.auth_token))
}
break;
@@ -2188,6 +2272,47 @@ $.ccio.globalWebsocket=function(d,user){
case'mjpeg':
$('#monitor_live_'+d.id+user.auth_token+' .stream-element').attr('src',$.ccio.init('location',user)+user.auth_token+'/mjpeg/'+d.ke+'/'+d.id+'/?full=true')
break;
+ case'h265':
+ var player = $.ccio.mon[d.ke+d.id+user.auth_token].h265Player
+ var video = $('#monitor_live_'+d.id+user.auth_token+' .stream-element')[0]
+ if (player) {
+ player.stop()
+ }
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265Player = new libde265.RawPlayer(video)
+ var player = $.ccio.mon[d.ke+d.id+user.auth_token].h265Player
+ player.set_status_callback(function(msg, fps) {
+ })
+ player.launch()
+ if($.ccio.mon[d.ke+d.id+user.auth_token].h265Socket && $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket.connected){
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket.disconnect()
+ }
+ if($.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream && $.ccio.mon[d.ke+d.id+user.auth_token].abort){
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream.abort()
+ }
+ if(d.d.stream_flv_type==='ws'){
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket = io(url,{transports: ['websocket'], forceNew: false})
+ var ws = $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket
+ ws.on('diconnect',function(){
+ console.log('h265Socket Stream Disconnected')
+ })
+ ws.on('connect',function(){
+ ws.emit('h265',{
+ url: url,
+ auth: user.auth_token,
+ uid: user.uid,
+ ke: d.ke,
+ id: d.id,
+ // channel: channel
+ })
+ ws.on('data',function(imageData){
+ player._handle_onChunk(imageData)
+ })
+ })
+ }else{
+ var url = $.ccio.init('location',user)+user.auth_token+'/h265/'+d.ke+'/'+d.id+'/s.hevc';
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream = player.createHttpStream(url)
+ }
+ break;
}
}
d.signal=parseFloat(d.d.signal_check);
@@ -2348,7 +2473,7 @@ $.ccio.globalWebsocket=function(d,user){
}
}
});
-
+
var eventsToCheck = Object.assign({},events)
$.each(data,function(m,b){
startTimeFormatted = $.ccio.timeObject(b.time).format('YYYY-MM-DD HH:mm:ss');
@@ -2836,7 +2961,7 @@ $.zO.initCanvas=function(){
$.zO.e.find('.cord_name').text(e.val)
$.zO.f.find('[name="sensitivity"]').val(e.cord.sensitivity)
$.zO.e.find('.canvas_holder canvas').remove();
-
+
$.zO.initLiveStream()
e.e=$.zO.ca.val(e.ar.join(','))
e.e.canvasAreaDraw({
@@ -2916,7 +3041,7 @@ $.zO.e.on('changed','#regions_canvas',function(e){
})
$.zO.f.submit(function(e){
e.preventDefault();e.e=$(this),e.s=e.e.serializeObject();
-
+
return false;
});
$('#regions_points')
@@ -2950,7 +3075,7 @@ $.pB.f.submit(function(e){
$.pB.o.empty();
$.pB.e.find('.stop').show();
$.pB.e.find('[type="submit"]').hide();
-
+
e.preventDefault();e.e=$(this),e.s=e.e.serializeObject();
e.s.url=e.s.url.trim();
var flags = '';
@@ -3111,7 +3236,7 @@ $.multimon.e.find('.import_config').click(function(){
newMon.details.auto_host = Monitor.Device
break;
case'remote':
-
+
break;
}
newMon.details = JSON.stringify(newMon.details)
@@ -3254,9 +3379,9 @@ $.each(<%-JSON.stringify(define["Monitor Settings"].blocks)%>,function(n,v){
}
if(b.name.indexOf('detail=')>-1){
b.name=b.name.replace('detail=','')
- v.element=$('[detail="'+b.name+'"]')
+ v.element=$.aM.e.find('[detail="'+b.name+'"]')
}else{
- v.element=$('[name="'+b.name+'"]')
+ v.element=$.aM.e.find('[name="'+b.name+'"]')
}
v.parent=v.element.parents('.form-group').find('label div:first-child span')
v.parent.find('small').remove()
@@ -3692,12 +3817,12 @@ $.aM.f.submit(function(ee){
var copyMonitors = $.aM.monitorsForCopy.val();
var chosenSections = [];
var chosenMonitors = {};
-
+
if(!copyMonitors||copyMonitors.length===0){
$.ccio.init('note',{title:'<%-cleanLang(lang['No Monitors Selected'])%>',text:'<%-cleanLang(lang.monSavedButNotCopied)%>'})
return
}
-
+
$.aM.e.find('[copy]').each(function(n,v){
var el = $(v)
if(el.val() === '1'){
@@ -3767,7 +3892,7 @@ $.aM.f.submit(function(ee){
})
console.log(chosenMonitors)
}
-
+
$.aM.e.modal('hide')
return false;
});
@@ -4078,16 +4203,152 @@ $.fI.f.submit(function(e){
e.er=[];
$.each(e.s,function(n,v){e.s[n]=v.trim()})
e.s.where=[];
- $('.where-row').each(function(n,v){
+ e.e.find('.where-row').each(function(n,v){
n={};
$(v).find('[where]').each(function(m,b){
b=$(b);
- n[b.attr('where')]=b.val();
+ n[b.attr('where')]=b.val().trim();
})
e.s.where.push(n)
})
$.ccio.cx({f:'settings',ff:'filters',fff:'save',form:e.s})
});
+//detector filters window
+$.detectorFilters={e:$('#detector_filter')};
+$.detectorFilters.f=$.detectorFilters.e.find('form');
+$.detectorFilters.md=$.detectorFilters.f.find('[detail]');
+$.detectorFilters.getSelected = function(){
+ return $('#detector_filters').val()
+}
+$.detectorFilters.drawOptions = function(){
+ var dFilters = $.detectorFilters.getCurrent()
+ $('#detector_filters optgroup').empty()
+ $.each(dFilters,function(n,dFilter){
+ $.ccio.tm('option',{auth_token:$user.auth_token,id:dFilter.id,name:dFilter.filter_name},'#detector_filters optgroup')
+ })
+}
+$.detectorFilters.getCurrent = function(){
+ try{
+ return JSON.parse($.aM.e.find('[detail="detector_filters"]').val())
+ }catch(err){
+ return {}
+ }
+}
+$.detectorFilters.save = function(){
+ var currentVals = $.detectorFilters.getCurrent()
+ currentVals[$.detectorFilters.lastSave.id] = $.detectorFilters.lastSave
+ $.aM.e.find('[detail="detector_filters"]').val(JSON.stringify(currentVals)).change()
+}
+$.ccio.tm('detector-filters-where');
+$.detectorFilters.e.on('change','[where="p1"]',function(e){
+ var el = $(this)
+ var p1v = el.val()
+ var parent = el.parents('.row')
+ var p3 = parent.find('[where="p3"]')
+ var options = []
+ switch(p1v){
+ case'reason':
+ options = [
+ 'licensePlate',
+ 'object',
+ 'motion',
+ ]
+ break;
+ case'plug':
+ options = [
+ 'PythonYolo',
+ 'OpenCV',
+ 'built-in',
+ ]
+ break;
+ case'tag':
+ options = [
+ 'car',
+ 'tree',
+ 'pottedplant',
+ ]
+ break;
+ }
+ var msg = 'Value'
+ if(options.length > 0){
+ msg = 'Example : '+options.join(', ')
+ }
+ p3.attr('placeholder',msg)
+})
+$.detectorFilters.e.on('shown.bs.modal',function(e){
+ $.detectorFilters.drawOptions()
+})
+$.detectorFilters.e.on('click','.where .add',function(e){
+ $.ccio.tm('detector-filters-where');
+})
+$.detectorFilters.e.on('click','.where .remove',function(e){
+ e.e=$('#detector_filters_where .row');
+ if(e.e.length>1){
+ e.e.last().remove();
+ $('#detector_filters_where .row:last [where="p4"]').prop('disabled',true)
+ }
+})
+$.detectorFilters.f.find('.delete').click(function(e){
+ var currentVals = $.detectorFilters.getCurrent()
+ var newObject = {}
+ var deleteId = $.detectorFilters.getSelected()
+ $.each(currentVals,function(id,obj){
+ if(id === deleteId)return false;
+ newObject[id] = obj
+ })
+ $.aM.e.find('[detail="detector_filters"]').val(JSON.stringify(newObject)).change()
+ $.detectorFilters.drawOptions()
+})
+$('#detector_filters').change(function(){
+ e = {}
+ e.e=$(this),e.id=e.e.val();
+ $('#detector_filters_where').empty()
+ if(e.id&&e.id!==''){
+ var currentFilter = $.detectorFilters.getCurrent()[e.id]
+ e.name=currentFilter.name;
+ $.each(currentFilter.where,function(n,v){
+ $.ccio.tm('detector-filters-where',v)
+ });
+ $.each(currentFilter.actions,function(action,val){
+ $.detectorFilters.e.find('[actions="'+action+'"]').val(val)
+ });
+ $.each(currentFilter,function(n,v){
+ if(n==='where'){return}
+ $.detectorFilters.f.find('[name="'+n+'"]').val(v);
+ });
+ }else{
+ e.name='<%-cleanLang(lang['Add New'])%>';
+ $.detectorFilters.f.find('[name="id"]').val($.ccio.gid(5));
+ $.ccio.tm('detector-filters-where');
+ }
+ $.detectorFilters.e.find('.filter_name').text(e.name)
+}).change()
+$.detectorFilters.f.submit(function(ee){
+ ee.preventDefault()
+ e = {}
+ e.e=$(this),e.s=e.e.serializeObject();
+ e.er=[];
+ $.each(e.s,function(n,v){e.s[n]=v.trim()})
+ //create conditions object (where)
+ e.s.where=[];
+ e.e.find('.where-row').each(function(n,v){
+ n={};
+ $(v).find('[where]').each(function(m,b){
+ b=$(b);
+ n[b.attr('where')]=b.val().trim();
+ })
+ e.s.where.push(n)
+ })
+ // create actions object (do)
+ e.s.actions={};
+ e.e.find('.actions-row').each(function(n,v){
+ b=$(v).find('[actions]');
+ e.s.actions[b.attr('actions')] = b.val()
+ })
+ $.detectorFilters.lastSave = e.s
+ $.detectorFilters.save()
+ $.detectorFilters.e.modal('hide')
+});
//settings window
$.sM={e:$('#settings')};
$.sM.f=$.sM.e.find('form');
@@ -4447,7 +4708,7 @@ $.timelapse.play = function(x){
clearInterval($.timelapse.interval)
videoNow.currentTime = videoNow.duration
}else{
- videoNow.currentTime += .5
+ videoNow.currentTime += .5
}
},500 / $.timelapse.playRate)
}
@@ -4625,7 +4886,7 @@ $.timelapse.e.on('click','[timelapse]',function(){
var vidTime = e.videoNow.duration * percentage;
e.videoNow.currentTime = vidTime;
});
-
+
$.ccio.log('$.timelapse',e.video)
$.timelapse.line.find('.timelapse_video').removeClass('active')
e.videoCurrentNow=$.timelapse.display.find('.videoNow')
@@ -4746,7 +5007,7 @@ $.pwrvid.e.on('click','[preview]',function(e){
})
if(e.status==1){
$.get($.ccio.init('videoHrefToRead',e.href),function(d){
-
+
})
}
var labels=[]
@@ -4877,7 +5138,7 @@ $.pwrvid.drawTimeline=function(getData){
e.eventLimit = $('#pvideo_event_limit').val();
if(e.eventLimit===''||isNaN(e.eventLimit)){e.eventLimit=500}
if(e.videoLimit===''||isNaN(e.videoLimit)){e.videoLimit=0}
-
+
var getTheData = function(){
e.live_header.text($.ccio.mon[$user.ke+mid+$user.auth_token].name)
e.live.attr('src',$.ccio.init('location',$user)+$user.auth_token+'/embed/'+$user.ke+'/'+mid+'/fullscreen|jquery|relative|gui')
@@ -4996,7 +5257,18 @@ $.grid.e
.on('gsresizestop', $.grid.saveElementPositions);
//open all monitors
$('[class_toggle="list-blocks"][data-target="#left_menu"]').dblclick(function(){
- $('#monitors_list [monitor="watch"]').click()
+ $('#monitors_list .monitor_block').each(function(n,v){
+ var el = $(v)
+ var ke = el.attr('ke')
+ var mid = el.attr('mid')
+ var auth = el.attr('auth')
+ var monItem = $('.monitor_item[ke='+ke+'][mid='+mid+'][auth='+auth+']')
+ if(monItem.length > 0){
+ monItem.find('[monitor="watch_on"]').click()
+ }else{
+ el.find('[monitor="watch"]').click()
+ }
+ })
})
//search monitors list
$('#monitors_list_search').keyup(function(){
@@ -5123,7 +5395,7 @@ $('body')
$.ccio.op(e.localStorage,e.value)
})
.on('click','[system]',function(e){
- var e={};
+ var e={};
e.e=$(this),
e.a=e.e.attr('system');//the function
switch(e.a){
@@ -5207,7 +5479,7 @@ $('body')
})
//monitor functions
.on('click','[monitor]',function(){
- var e={};
+ var e={};
e.e=$(this),
e.a=e.e.attr('monitor'),//the function
e.p=e.e.parents('[mid]'),//the parent element for monitor item
@@ -5310,7 +5582,7 @@ $('body')
e.d.detector_scale_x=e.width.val();
e.d.detector_scale_y=e.height.val();
}
-
+
$.zO.e.modal('show');
$.zO.o().attr('width',e.d.detector_scale_x).attr('height',e.d.detector_scale_y);
$.zO.c.css({width:e.d.detector_scale_x,height:e.d.detector_scale_y});
@@ -5325,6 +5597,9 @@ $('body')
$.zO.regionViewerDetails=e.d;
$.zO.initRegionList()
break;
+ case'detector_filters':
+ $.detectorFilters.e.modal('show');
+ break;
case'snapshot':
$.ccio.snapshot(e,function(url){
$('#temp').html('a ').find('a')[0].click();
@@ -5673,4 +5948,4 @@ function onFullScreenChange() {
$('canvas.stream-element').resize();
},2000)
}
-}
\ No newline at end of file
+}
diff --git a/web/pages/blocks/detectorfilters.ejs b/web/pages/blocks/detectorfilters.ejs
new file mode 100644
index 0000000..bab546f
--- /dev/null
+++ b/web/pages/blocks/detectorfilters.ejs
@@ -0,0 +1,249 @@
+
+
\ No newline at end of file
diff --git a/web/pages/blocks/monitoredit.ejs b/web/pages/blocks/monitoredit.ejs
index 86b7c29..52063b3 100644
--- a/web/pages/blocks/monitoredit.ejs
+++ b/web/pages/blocks/monitoredit.ejs
@@ -26,7 +26,7 @@
<%-lang.IdentityText2%>