From 157bf6feb0c104dc7c483f8b10f71effdad9513a Mon Sep 17 00:00:00 2001 From: Moe Date: Thu, 23 Aug 2018 14:13:06 -0700 Subject: [PATCH] Teenage Turtle --- COPYING => COPYING.md | 0 LICENSE => LICENSE.md | 0 camera.js | 1162 ++++++++++++++++---------- conf.sample.json | 3 +- definitions/en_CA.json | 6 +- languages/en_CA.json | 20 + languages/fr.json | 4 + web/libs/css/main.dash2.css | 1 + web/libs/js/main.dash2.js | 335 +++++++- web/pages/blocks/detectorfilters.ejs | 249 ++++++ web/pages/blocks/monitoredit.ejs | 173 ++-- web/pages/blocks/settings.ejs | 53 +- web/pages/embed.ejs | 83 +- web/pages/home.ejs | 4 +- 14 files changed, 1530 insertions(+), 563 deletions(-) rename COPYING => COPYING.md (100%) rename LICENSE => LICENSE.md (100%) create mode 100644 web/pages/blocks/detectorfilters.ejs 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+='
' + tmp+=' ' + tmp+='
' + tmp+='
' + tmp+=' ' + tmp+='
' + 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+=' ' tmp+=' ' tmp+=' ' + tmp+=' ' 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%>
-
@@ -208,7 +208,7 @@
-
+
@@ -289,9 +294,9 @@
-
+
-
+
@@ -351,7 +357,7 @@
-
+
-
+
@@ -447,8 +453,8 @@
@@ -545,12 +551,12 @@
@@ -578,8 +584,8 @@
@@ -606,6 +612,7 @@ + @@ -618,12 +625,12 @@
@@ -705,8 +712,8 @@
@@ -785,7 +792,7 @@

<%-lang['Custom']%>

@@ -1005,13 +1012,30 @@
+
+ +
+
+ +
  <%-lang['Region Editor']%> @@ -1090,7 +1114,7 @@
-
+

<%-lang['Object Detection']%> <%-lang['Plugin']%> : <%-lang['Not Connected']%>

+
+
+
+ +
+
+ +
+
+ +
+
+
-
\ No newline at end of file +
diff --git a/web/pages/blocks/settings.ejs b/web/pages/blocks/settings.ejs index beebb41..f3dce57 100644 --- a/web/pages/blocks/settings.ejs +++ b/web/pages/blocks/settings.ejs @@ -24,6 +24,33 @@
+
+

<%-lang['2-Factor Authentication']%>

+
+ +
+
+ +
+
+ +
+

<%-lang.Profile%>

@@ -41,14 +68,6 @@
-
- -
<% if(!details.sub){ %> <% if(details.edit_size!=='0'){ %>
@@ -116,11 +135,19 @@
-
- -
+
+ +
+
+ +
<% if(!details.sub){ %>
diff --git a/web/pages/embed.ejs b/web/pages/embed.ejs index baaf18a..fb30db3 100644 --- a/web/pages/embed.ejs +++ b/web/pages/embed.ejs @@ -20,6 +20,7 @@ if(data.url.charAt(data.url.length - 1) !== '/'){ + <% if(data.addon){ var ar={} decodeURI(data.addon).split('|').forEach(function(v){ @@ -133,7 +134,7 @@ $(document).ready(function(){ })
- + <% switch(mon.details.stream_type){ case'jpeg': %><% @@ -148,8 +149,8 @@ $(document).ready(function(){ %><% break; } %> - - + + <% if(data.addon&&data.addon.indexOf('gui')>-1){ %>
@@ -170,11 +171,30 @@ $(document).ready(function(){ $.shinobi.mon={} }; $.shinobi.init=function(d){ + if($.shinobi.mon[d.id].Base64 && $.shinobi.mon[d.id].Base64.connected){ + $.shinobi.mon[d.id].Base64.disconnect() + } + if($.shinobi.mon[d.id].Poseidon){ + $.shinobi.mon[d.id].Poseidon.destroy() + } + if ($.shinobi.mon[d.id].h265Player) { + $.shinobi.mon[d.id].h265Player.stop() + } + if($.shinobi.mon[d.id].h265Socket && $.shinobi.mon[d.id].h265Socket.connected){ + $.shinobi.mon[d.id].h265Socket.disconnect() + } + if($.shinobi.mon[d.id].h265HttpStream && $.shinobi.mon[d.id].abort){ + $.shinobi.mon[d.id].h265HttpStream.abort() + } + if($.shinobi.mon[d.id].flv){ + $.shinobi.mon[d.id].flv.destroy() + } + if($.shinobi.mon[d.id].hls){ + $.shinobi.mon[d.id].hls.destroy() + } + clearInterval($.shinobi.mon[d.id].jpegInterval); switch($.shinobi.mon[d.id].details.stream_type){ case'b64': - if($.shinobi.mon[d.id].Base64 && $.shinobi.mon[d.id].Base64.connected){ - $.shinobi.mon[d.id].Base64.disconnect() - } $.shinobi.mon[d.id].Base64 = io('<%=data.url%>',{transports: ['websocket'], forceNew: false}) var ws = $.shinobi.mon[d.id].Base64 ws.on('diconnect',function(){ @@ -209,7 +229,7 @@ $(document).ready(function(){ // } // ctx.getContext("2d").drawImage(image,d.x,d.y,d.width,d.height) ctx.getContext("2d").drawImage(image,d.x,d.y,ctx.width,ctx.height) - URL.revokeObjectURL($.ccio.mon[d.ke+d.id+user.auth_token].imageUrl) + URL.revokeObjectURL($.shinobi.mon[d.id].imageUrl) } ws.on('data',function(imageData){ try{ @@ -232,9 +252,6 @@ $(document).ready(function(){ case'mp4': var stream = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element'); if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){ - if($.shinobi.mon[d.id].Poseidon){ - $.shinobi.mon[d.id].Poseidon.destroy() - } var createPoseidon = function(){ $.shinobi.mon[d.id].Poseidon = new Poseidon({ video: stream[0], @@ -261,11 +278,38 @@ $(document).ready(function(){ stream.attr('src','<%=data.url%><%=data.auth%>/mp4/'+d.ke+'/'+d.id+'/s.mp4') } break; + case'h265': + var player = $.shinobi.mon[d.id].h265Player + var video = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0] + player = new libde265.RawPlayer(video) + player.set_status_callback(function(msg, fps) { + }) + player.launch() + if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){ + $.shinobi.mon[d.id].h265Socket = io(url,{transports: ['websocket'], forceNew: false}) + var ws = $.shinobi.mon[d.id].h265Socket + ws.on('diconnect',function(){ + console.log('h265Socket Stream Disconnected') + }) + ws.on('connect',function(){ + ws.emit('h265',{ + auth:'<%=data.auth%>', + ke:d.ke, + uid:'<%=data.uid%>', + id:d.id, + url: '<%=data.url%>' + // channel: channel + }) + ws.on('data',function(imageData){ + player._handle_onChunk(imageData) + }) + }) + }else{ + $.shinobi.mon[d.id].h265HttpStream = player.createHttpStream('<%=data.url%><%=data.auth%>/h265/'+d.ke+'/'+d.id+'/s.hevc') + } + break; case'flv': if (flvjs.isSupported()) { - if($.shinobi.mon[d.id].flv){ - $.shinobi.mon[d.id].flv.destroy() - } var options = {}; // if($.shinobi.mon[d.id].details.stream_flv_type==='ws'){ // if($.shinobi.mon[d.id].details.stream_flv_maxLatency&&$.shinobi.mon[d.id].details.stream_flv_maxLatency!==''){ @@ -307,9 +351,9 @@ $(document).ready(function(){ k=d.mon.details; k.jpegInterval=parseFloat(k.jpegInterval); if(!k.jpegInterval||k.jpegInterval===''||isNaN(k.jpegInterval)){k.jpegInterval=1} - if(!$.shinobi.mon[d.mon.mid].jpegInterval){ - clearInterval($.shinobi.mon[d.mon.mid].jpegInterval); - $.shinobi.mon[d.mon.mid].jpegInterval=setInterval(function(){ + if(!$.shinobi.mon[d.id].jpegInterval){ + clearInterval($.shinobi.mon[d.id].jpegInterval); + $.shinobi.mon[d.id].jpegInterval=setInterval(function(){ $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element').attr('src','<%=data.url%><%=data.auth%>/jpeg/'+d.mon.ke+'/'+d.mon.mid+'/s.jpg?time='+(new Date()).getTime()) },1000/k.jpegInterval); } @@ -317,9 +361,6 @@ $(document).ready(function(){ case'hls': var video = $('#SHINOBI_'+d.ke+'_'+d.id+' .stream-element')[0]; d.url='<%=data.url%><%=data.auth%>/hls/'+d.ke+'/'+d.id+'/s.m3u8'; - if($.shinobi.mon[d.id].hls){ - $.shinobi.mon[d.id].hls.destroy() - } if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)||(navigator.userAgent.match(/(Safari)/)&&!navigator.userAgent.match('Chrome'))) { video.src=d.url; video.play(); @@ -386,7 +427,7 @@ $(document).ready(function(){ $(window).resize(); // d.mon.details=JSON.stringify(d.mon.details); d.mon.id = d.mon.mid - $.shinobi.mon[d.mon.mid]=d.mon; + $.shinobi.mon[d.id] = d.mon; $.shinobi.init(d.mon); break; } @@ -413,4 +454,4 @@ $(document).ready(function(){ $.shinobi.init(monitor); }) $('.shinobi_ws_http_toggle').show() - \ No newline at end of file + diff --git a/web/pages/home.ejs b/web/pages/home.ejs index d2595bc..0beba29 100644 --- a/web/pages/home.ejs +++ b/web/pages/home.ejs @@ -163,6 +163,7 @@ <% include blocks/monitoredit.ejs %> <% include blocks/probe.ejs %> <% include blocks/region.ejs %> +<% include blocks/detectorfilters.ejs %> <% include blocks/confirm.ejs %> <% if(config.DropboxAppKey){ %> @@ -179,6 +180,7 @@ + @@ -193,4 +195,4 @@ -<% include blocks/help.ejs %> \ No newline at end of file +<% include blocks/help.ejs %>