Add JPEG-based Timelapse creation
- Currently can only retrieve data about Timelapse through API, GUI coming soon + Definition fixesmerge-requests/63/head
parent
040080fd0b
commit
00ab3046ff
|
|
@ -18,6 +18,7 @@ module.exports = function(s,config,lang){
|
|||
"description": "This is the primary task of the monitor.",
|
||||
"default": "stop",
|
||||
"example": "",
|
||||
"selector": "h_m",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.Disabled,
|
||||
|
|
@ -45,13 +46,13 @@ module.exports = function(s,config,lang){
|
|||
"name": "mid",
|
||||
"field": lang["Monitor ID"],
|
||||
"description": "This is a non-changeable identifier for the monitor. You can duplicate a monitor by double clicking the Monitor ID and changing it.",
|
||||
"example": s.gid(),
|
||||
"example": s.gid()
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"field": lang.Name,
|
||||
"description": "This is the human-readable display name for the monitor.",
|
||||
"example": "Bunny",
|
||||
"example": "Bunny"
|
||||
},
|
||||
{
|
||||
"name": "detail=max_keep_days",
|
||||
|
|
@ -1242,7 +1243,7 @@ module.exports = function(s,config,lang){
|
|||
"possible": ""
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
"Recording": {
|
||||
"id": "monSectionRecording",
|
||||
"name": lang.Recording,
|
||||
|
|
@ -1251,6 +1252,7 @@ module.exports = function(s,config,lang){
|
|||
"input-mapping": "record",
|
||||
"blockquote": lang.RecordingText,
|
||||
"blockquoteClass": 'global_tip',
|
||||
"section-class": 'h_m_input h_m_record h_m_idle',
|
||||
"info": [
|
||||
// {
|
||||
// "name": "height",
|
||||
|
|
@ -1707,6 +1709,111 @@ module.exports = function(s,config,lang){
|
|||
},
|
||||
]
|
||||
},
|
||||
"Timelapse": {
|
||||
"name": lang['Timelapse'],
|
||||
"id": "monSectionTimelapse",
|
||||
"color": "red",
|
||||
"isSection": true,
|
||||
"input-mapping": "record_timelapse",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=record_timelapse",
|
||||
"field": lang.Enabled,
|
||||
"description": "Create a JPEG based timelapse.",
|
||||
"default": "0",
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"selector": "h_rec_ti",
|
||||
"possible": [
|
||||
{
|
||||
"name": "No",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "Yes",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=record_timelapse_mp4",
|
||||
"field": lang.Enabled,
|
||||
"description": "Create an MP4 file at the end of each day for the timelapse.",
|
||||
"default": "0",
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": "No",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "Yes",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=record_timelapse_fps",
|
||||
"field": lang['Creation Interval'],
|
||||
"description": "",
|
||||
"default": "900",
|
||||
"example": "",
|
||||
"form-group-class": "h_rec_ti_input h_rec_ti_1",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": `15 ${lang.minutes}`,
|
||||
"value": "900"
|
||||
},
|
||||
{
|
||||
"name": `30 ${lang.minutes}`,
|
||||
"value": "1800"
|
||||
},
|
||||
{
|
||||
"name": `45 ${lang.minutes}`,
|
||||
"value": "2700"
|
||||
},
|
||||
{
|
||||
"name": `60 ${lang.minutes}`,
|
||||
"value": "3600"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=record_timelapse_scale_x",
|
||||
"field": lang['Image Width'],
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"form-group-class": "h_rec_ti_input h_rec_ti_1",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=record_timelapse_scale_y",
|
||||
"field": lang['Image Height'],
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"form-group-class": "h_rec_ti_input h_rec_ti_1",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=record_timelapse_vf",
|
||||
"field": lang['Video Filter'],
|
||||
"description": "",
|
||||
"default": "",
|
||||
"example": "",
|
||||
"form-group-class": "h_rec_ti_input h_rec_ti_1",
|
||||
"possible": ""
|
||||
},
|
||||
]
|
||||
},
|
||||
"Custom": {
|
||||
"name": "Custom",
|
||||
"color": "navy",
|
||||
|
|
@ -1976,16 +2083,6 @@ module.exports = function(s,config,lang){
|
|||
{
|
||||
hidden: true,
|
||||
"name": "detail=detector_send_video_length",
|
||||
"field": lang['Notification Video Length'],
|
||||
"description": "",
|
||||
"default": "10",
|
||||
"example": "",
|
||||
"form-group-class": "h_det_input h_det_1",
|
||||
"form-group-class-pre-layer": "h_rec_mtd_input h_rec_mtd_hot h_rec_mtd_sip",
|
||||
"possible": ""
|
||||
},
|
||||
{
|
||||
"name": "detail=detector_send_video_length",
|
||||
"field": lang["Notification Video Length"],
|
||||
"description": "In seconds. The length of the video that gets sent to your Notification service, like Email or Discord.",
|
||||
"default": "10",
|
||||
|
|
@ -2522,15 +2619,13 @@ module.exports = function(s,config,lang){
|
|||
]
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": lang['Object Detection'],
|
||||
"color": "orange",
|
||||
id: "monSectionDetectorObject",
|
||||
headerTitle: `${lang['Object Detection']} <small>${lang['Plugin']} : <b class="shinobi-detector_name"></b> <b class="shinobi-detector-invert">${lang['Not Connected']}</b><b class="shinobi-detector" style="display:none">${lang['Connected']}</b></small>`,
|
||||
headerTitle: `${lang['Object Detection']} <small><b class="shinobi-detector_name"></b> <b class="shinobi-detector-invert">${lang['Not Connected']}</b><b class="shinobi-detector" style="display:none">${lang['Connected']}</b></small>`,
|
||||
isFormGroupGroup: true,
|
||||
isSection: true,
|
||||
"section-pre-class": "h_det_input h_det_1",
|
||||
"section-class": "shinobi-detector-opencv shinobi-detector-openalpr shinobi-detector-yolo shinobi-detector-dlib shinobi-detector_plug",
|
||||
"section-class": "h_det_input h_det_1",
|
||||
"info": [
|
||||
{
|
||||
"name": "detail=detector_use_detect_object",
|
||||
|
|
@ -2723,7 +2818,7 @@ module.exports = function(s,config,lang){
|
|||
hidden: true,
|
||||
"name": lang['Traditional Recording'],
|
||||
"color": "orange",
|
||||
id: "monSectionLisencePlateDetector",
|
||||
id: "monSectionDetectorTraditionalRecording",
|
||||
isSection: true,
|
||||
isAdvanced: true,
|
||||
isFormGroupGroup: true,
|
||||
|
|
|
|||
|
|
@ -389,6 +389,9 @@
|
|||
"Detector Recording Complete": "Detector Recording Complete",
|
||||
"Clear Recorder Process": "Clear Recorder Process",
|
||||
"Logging": "Logging",
|
||||
"Timelapse": "Timelapse",
|
||||
"Creation Interval": "Creation Interval",
|
||||
"Plugin": "Plugin",
|
||||
"IdentityText1": "This is how the system will identify the data for this stream. You cannot change the <b>Monitor ID</b> once you have pressed save. If you want you can make the <b>Monitor ID</b> more human readable before you continue.",
|
||||
"IdentityText2": "You can duplicate a monitor by modifying the <b>Monitor ID</b> then pressing save. You <b>cannot</b> use the ID of a monitor that already exists or it will save over that monitor's database information.",
|
||||
"opencvCascadesText": "If you see nothing here then just download this package of <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascades</a>. Drop them into <code>plugins/opencv/cascades</code> then press refresh <i class=\"fa fa-retweet\"></i>.",
|
||||
|
|
|
|||
|
|
@ -905,6 +905,24 @@ module.exports = function(s,config,lang,onFinish){
|
|||
x.pipe += ' -q:v 1 -an -c:v copy -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'coProcessor.m3u8"'
|
||||
}
|
||||
}
|
||||
ffmpeg.buildTimelapseOutput = function(e,x){
|
||||
if(e.details.record_timelapse === '1'){
|
||||
if(e.details.input_map_choices&&e.details.input_map_choices.record_timelapse){
|
||||
//add input feed map
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.record_timelapse)
|
||||
}
|
||||
var flags = []
|
||||
if(e.details.record_timelapse_fps && e.details.record_timelapse_fps !== ''){
|
||||
flags.push('-r 1/' + e.details.record_timelapse_fps)
|
||||
}else{
|
||||
flags.push('-r 1/900') // 15 minutes
|
||||
}
|
||||
if(e.details.record_timelapse_vf && e.details.record_timelapse_vf !== '')flags.push('-vf ' + e.details.record_timelapse_vf)
|
||||
if(e.details.record_timelapse_scale_x && e.details.record_timelapse_scale_x !== '' && e.details.record_timelapse_scale_y && e.details.record_timelapse_scale_y !== '')flags.push(`-s ${e.details.record_timelapse_scale_x}x${e.details.record_timelapse_scale_y}`)
|
||||
// x.pipe+=` -strftime 1 ${flags.join(' ')} -an -q:v 1 "${e.dirTimelapse}%Y-%m-%d/%Y-%m-%dT%H-%M-%S.jpg"`
|
||||
x.pipe+=` -f singlejpeg ${flags.join(' ')} -an -q:v 1 pipe:7`
|
||||
}
|
||||
}
|
||||
ffmpeg.assembleMainPieces = function(e,x){
|
||||
//create executeable FFMPEG command
|
||||
x.ffmpegCommandString = x.loglevel+x.input_fps;
|
||||
|
|
@ -962,6 +980,7 @@ module.exports = function(s,config,lang,onFinish){
|
|||
ffmpeg.buildAudioDetector(e,x)
|
||||
ffmpeg.buildMainDetector(e,x)
|
||||
ffmpeg.buildCoProcessorFeed(e,x)
|
||||
ffmpeg.buildTimelapseOutput(e,x)
|
||||
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
|
||||
extender(e,x)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -625,13 +625,15 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
s.createCameraFolders = function(e){
|
||||
//set the recording directory
|
||||
var monitorRecordingFolder
|
||||
if(e.details && e.details.dir && e.details.dir !== '' && config.childNodes.mode !== 'child'){
|
||||
//addStorage choice
|
||||
e.dir=s.checkCorrectPathEnding(e.details.dir)+e.ke+'/';
|
||||
if (!fs.existsSync(e.dir)){
|
||||
fs.mkdirSync(e.dir);
|
||||
}
|
||||
e.dir=e.dir+e.id+'/';
|
||||
monitorRecordingFolder = e.dir + e.id + ''
|
||||
e.dir = monitorRecordingFolder + '/'
|
||||
if (!fs.existsSync(e.dir)){
|
||||
fs.mkdirSync(e.dir);
|
||||
}
|
||||
|
|
@ -645,6 +647,12 @@ module.exports = function(s,config,lang){
|
|||
if (!fs.existsSync(e.dir)){
|
||||
fs.mkdirSync(e.dir);
|
||||
}
|
||||
monitorRecordingFolder = s.dir.videos + e.ke + '/' + e.id
|
||||
}
|
||||
//
|
||||
e.dirTimelapse = monitorRecordingFolder + '_timelapse/'
|
||||
if (!fs.existsSync(e.dirTimelapse)){
|
||||
fs.mkdirSync(e.dirTimelapse)
|
||||
}
|
||||
// exec('chmod -R 777 '+e.dir,function(err){
|
||||
//
|
||||
|
|
@ -896,6 +904,52 @@ module.exports = function(s,config,lang){
|
|||
audioDetector.start()
|
||||
s.group[e.ke].mon[e.id].spawn.stdio[6].pipe(audioDetector.streamDecoder)
|
||||
}
|
||||
if(e.details.record_timelapse === '1'){
|
||||
s.group[e.ke].mon[e.id].spawn.stdio[7].on('data',function(data){
|
||||
var fileStream = s.group[e.ke].mon[e.id].recordTimelapseWriter
|
||||
if(!fileStream){
|
||||
var currentDate = s.formattedTime(null,'YYYY-MM-DD')
|
||||
var filename = s.formattedTime() + '.jpg'
|
||||
var location = e.dirTimelapse + currentDate + '/'
|
||||
if(!fs.existsSync(location)){
|
||||
fs.mkdirSync(location)
|
||||
}
|
||||
fileStream = fs.createWriteStream(location + filename)
|
||||
fileStream.on('close', function () {
|
||||
s.group[e.ke].mon[e.id].recordTimelapseWriter = null
|
||||
var fileStats = fs.statSync(location + filename)
|
||||
var fileInfo = {}
|
||||
if(e.details && e.details.dir && e.details.dir !== ''){
|
||||
fileInfo.dir = e.details.dir
|
||||
}
|
||||
fileInfo.size = fileStats.size
|
||||
s.sqlQuery('SELECT * FROM Timelapses WHERE ke=? AND mid=? AND date=?',[e.ke,e.id,currentDate],function(err,rows){
|
||||
if(rows && rows[0]){
|
||||
var row = rows[0]
|
||||
var details = s.parseJSON(row.details)
|
||||
details.files[filename] = fileInfo
|
||||
row.size += fileStats.size
|
||||
s.sqlQuery('UPDATE Timelapses SET details=?,size=? WHERE ke=? AND mid=? AND date=?',[s.s(details),row.size,e.ke,e.id,currentDate],function(){
|
||||
|
||||
})
|
||||
}else{
|
||||
var details = {
|
||||
files: {}
|
||||
}
|
||||
details.files[filename] = fileInfo
|
||||
s.sqlQuery('INSERT INTO Timelapses (ke,mid,details,date,size) VALUES (?,?,?,?,?)',[e.ke,e.id,s.s(details),currentDate,fileStats.size])
|
||||
}
|
||||
})
|
||||
})
|
||||
s.group[e.ke].mon[e.id].recordTimelapseWriter = fileStream
|
||||
}
|
||||
fileStream.write(data)
|
||||
clearTimeout(s.group[e.ke].mon[e.id].recordTimelapseWriterTimeout)
|
||||
s.group[e.ke].mon[e.id].recordTimelapseWriterTimeout = setTimeout(function(){
|
||||
fileStream.end()
|
||||
},900)
|
||||
})
|
||||
}
|
||||
if(e.details.detector === '1' && e.coProcessor === false){
|
||||
s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke})
|
||||
//frames from motion detect
|
||||
|
|
|
|||
15
libs/sql.js
15
libs/sql.js
|
|
@ -46,8 +46,12 @@ module.exports = function(s,config){
|
|||
}
|
||||
return newQuery
|
||||
}
|
||||
s.getUnixDate = function(value){
|
||||
newValue = new Date(value).valueOf()
|
||||
return newValue
|
||||
}
|
||||
s.stringToSqlTime = function(value){
|
||||
newValue = new Date(value.replace('T',' '))
|
||||
newValue = s.getUnixDate(s.nameToTime(value))
|
||||
return newValue
|
||||
}
|
||||
s.sqlQuery = function(query,values,onMoveOn,hideLog){
|
||||
|
|
@ -57,6 +61,11 @@ module.exports = function(s,config){
|
|||
var values = [];
|
||||
}
|
||||
if(!onMoveOn){onMoveOn=function(){}}
|
||||
// if(s.databaseOptions.client === 'pg'){
|
||||
// query = query
|
||||
// .replace(/ NOT LIKE /g," NOT ILIKE ")
|
||||
// .replace(/ LIKE /g," ILIKE ")
|
||||
// }
|
||||
var mergedQuery = s.mergeQueryValues(query,values)
|
||||
s.debugLog('s.sqlQuery QUERY',mergedQuery)
|
||||
if(!s.databaseEngine || !s.databaseEngine.raw){
|
||||
|
|
@ -109,6 +118,10 @@ module.exports = function(s,config){
|
|||
s.sqlQuery("CREATE TABLE IF NOT EXISTS `Schedules` (`ke` varchar(50) DEFAULT NULL,`name` text,`details` text,`start` varchar(10) DEFAULT NULL,`end` varchar(10) DEFAULT NULL,`enabled` int(1) NOT NULL DEFAULT '1')" + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
},true)
|
||||
//add Schedules table, will remove in future
|
||||
s.sqlQuery("CREATE TABLE IF NOT EXISTS `Timelapses` (`ke`varchar(50)NOT NULL,`mid`varchar(50)NOT NULL,`details`longtext,`date`date NOT NULL,`time`timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`end`timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`size`int(11)NOT NULL)" + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
},true)
|
||||
//add Cloud Videos table, will remove in future
|
||||
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\',`details` text)' + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
|
|
|
|||
|
|
@ -1094,6 +1094,165 @@ module.exports = function(s,config,lang,app,io){
|
|||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Timelapse images
|
||||
*/
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke',
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id',
|
||||
], function (req,res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
|
||||
if(
|
||||
user.permissions.watch_videos==="0" ||
|
||||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
|
||||
){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
req.sql='SELECT * FROM `Timelapses` WHERE ke=?';req.ar=[req.params.ke];
|
||||
if(req.query.archived=='1'){
|
||||
req.sql+=' AND details LIKE \'%"archived":"1"\''
|
||||
}
|
||||
if(!req.params.id){
|
||||
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
|
||||
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
|
||||
req.or=[];
|
||||
user.details.monitors.forEach(function(v,n){
|
||||
req.or.push('mid=?');req.ar.push(v)
|
||||
})
|
||||
req.sql+=' AND ('+req.or.join(' OR ')+')'
|
||||
}
|
||||
}else{
|
||||
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
|
||||
req.sql+=' and mid=?'
|
||||
req.ar.push(req.params.id)
|
||||
}else{
|
||||
res.end('[]');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(req.query.start||req.query.end){
|
||||
if(req.query.start && req.query.start !== ''){
|
||||
req.query.start = s.stringToSqlTime(req.query.start)
|
||||
}
|
||||
if(req.query.end && req.query.end !== ''){
|
||||
req.query.end = s.stringToSqlTime(req.query.end)
|
||||
}
|
||||
if(!req.query.startOperator||req.query.startOperator==''){
|
||||
req.query.startOperator='>='
|
||||
}
|
||||
if(!req.query.endOperator||req.query.endOperator==''){
|
||||
req.query.endOperator='<='
|
||||
}
|
||||
var endIsStartTo
|
||||
var theEndParameter = '`end`'
|
||||
if(req.query.endIsStartTo){
|
||||
endIsStartTo = true
|
||||
theEndParameter = '`time`'
|
||||
}
|
||||
switch(true){
|
||||
case(req.query.start&&req.query.start!==''&&req.query.end&&req.query.end!==''):
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ? AND '+theEndParameter+' '+req.query.endOperator+' ?'
|
||||
req.ar.push(req.query.start)
|
||||
req.ar.push(req.query.end)
|
||||
break;
|
||||
case(req.query.start&&req.query.start!==''):
|
||||
req.sql+=' AND `time` '+req.query.startOperator+' ?'
|
||||
req.ar.push(req.query.start)
|
||||
break;
|
||||
case(req.query.end&&req.query.end!==''):
|
||||
req.sql+=' AND '+theEndParameter+' '+req.query.endOperator+' ?'
|
||||
req.ar.push(req.query.end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
req.sql+=' ORDER BY `time` DESC'
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
if(!r){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
r.forEach(function(row){
|
||||
row.details = s.parseJSON(row.details)
|
||||
})
|
||||
res.end(s.prettyPrint(r))
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Timelapse images
|
||||
*/
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date',
|
||||
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date/:filename',
|
||||
], function (req,res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
var hasRestrictions = user.details.sub && user.details.allmonitors !== '1'
|
||||
if(
|
||||
user.permissions.watch_videos==="0" ||
|
||||
hasRestrictions && (!user.details.video_view || user.details.video_view.indexOf(req.params.id)===-1)
|
||||
){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
req.sql='SELECT * FROM `Timelapses` WHERE ke=?';req.ar=[req.params.ke];
|
||||
if(req.query.archived=='1'){
|
||||
req.sql+=' AND details LIKE \'%"archived":"1"\''
|
||||
}
|
||||
if(!req.params.id){
|
||||
if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){
|
||||
try{user.details.monitors=JSON.parse(user.details.monitors);}catch(er){}
|
||||
req.or=[];
|
||||
user.details.monitors.forEach(function(v,n){
|
||||
req.or.push('mid=?');req.ar.push(v)
|
||||
})
|
||||
req.sql+=' AND ('+req.or.join(' OR ')+')'
|
||||
}
|
||||
}else{
|
||||
if(!user.details.sub||user.details.allmonitors!=='0'||user.details.monitors.indexOf(req.params.id)>-1){
|
||||
req.sql+=' and mid=?'
|
||||
req.ar.push(req.params.id)
|
||||
}else{
|
||||
res.end('[]');
|
||||
return;
|
||||
}
|
||||
}
|
||||
req.sql+=' and date=?'
|
||||
req.ar.push(req.params.date)
|
||||
req.sql+=' ORDER BY `time` DESC'
|
||||
s.sqlQuery(req.sql,req.ar,function(err,r){
|
||||
if(!r || !r[0]){
|
||||
res.end(s.prettyPrint([]))
|
||||
return
|
||||
}
|
||||
var timelapse = r[0]
|
||||
timelapse.details = s.parseJSON(timelapse.details)
|
||||
if(req.params.filename){
|
||||
var fileInfo = timelapse.details.files[req.params.filename]
|
||||
if(fileInfo){
|
||||
res.contentType('image/jpeg')
|
||||
var fileLocation
|
||||
var currentDate = req.params.date
|
||||
if(fileInfo.dir){
|
||||
fileLocation = `${s.checkCorrectPathEnding(fileInfo.dir)}`
|
||||
}else{
|
||||
fileLocation = `${s.dir.videos}`
|
||||
}
|
||||
fileLocation = `${fileLocation}${timelapse.ke}/${timelapse.mid}_timelapse/${currentDate}/${req.params.filename}`
|
||||
res.on('finish',function(){res.end()})
|
||||
fs.createReadStream(fileLocation).pipe(res)
|
||||
}else{
|
||||
res.end(s.prettyPrint({ok: false, msg: lang['File Not Exist']}))
|
||||
}
|
||||
}else{
|
||||
res.end(s.prettyPrint(timelapse))
|
||||
}
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Events
|
||||
*/
|
||||
app.get([config.webPaths.apiPrefix+':auth/events/:ke',config.webPaths.apiPrefix+':auth/events/:ke/:id',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start/:end'], function (req,res){
|
||||
|
|
|
|||
|
|
@ -42,3 +42,4 @@ form.modal-body{margin:0}
|
|||
.form-group-group.grey{border-color:#777}
|
||||
.form-group-group.grey > h4{background:#777;color:#fff}
|
||||
.dark .form-group-group{background:#222}
|
||||
.form-group-group:last-child {margin-bottom: 0}
|
||||
|
|
|
|||
Loading…
Reference in New Issue