2019-06-07 16:52:07 +00:00
|
|
|
var fs = require('fs')
|
2019-07-10 06:12:17 +00:00
|
|
|
var moment = require('moment')
|
2019-06-07 16:52:07 +00:00
|
|
|
var express = require('express')
|
|
|
|
module.exports = function(s,config,lang,app,io){
|
|
|
|
s.getTimelapseFrameDirectory = function(e){
|
|
|
|
if(e.mid&&!e.id){e.id=e.mid}
|
|
|
|
s.checkDetails(e)
|
|
|
|
if(e.details&&e.details.dir&&e.details.dir!==''){
|
|
|
|
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'_timelapse/'
|
|
|
|
}else{
|
|
|
|
return s.dir.videos+e.ke+'/'+e.id+'_timelapse/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.createTimelapseFrameAndInsert = function(e,location,filename){
|
|
|
|
//e = monitor object
|
|
|
|
//location = file location
|
|
|
|
var filePath = location + filename
|
|
|
|
var fileStats = fs.statSync(filePath)
|
|
|
|
var details = {}
|
|
|
|
if(e.details && e.details.dir && e.details.dir !== ''){
|
|
|
|
details.dir = e.details.dir
|
|
|
|
}
|
|
|
|
var timeNow = new Date()
|
|
|
|
var queryInfo = {
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
|
|
|
details: s.s(details),
|
|
|
|
filename: filename,
|
|
|
|
size: fileStats.size,
|
|
|
|
time: timeNow
|
|
|
|
}
|
|
|
|
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
|
|
|
|
var currentDate = s.formattedTime(queryInfo.time,'YYYY-MM-DD')
|
|
|
|
s.cx({
|
|
|
|
f: 'open_timelapse_file_transfer',
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
2019-07-08 03:39:41 +00:00
|
|
|
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
2019-06-07 16:52:07 +00:00
|
|
|
filename: filename,
|
|
|
|
currentDate: currentDate,
|
|
|
|
queryInfo: queryInfo
|
|
|
|
})
|
|
|
|
var formattedTime = s.timeObject(timeNow).format()
|
|
|
|
fs.createReadStream(filePath,{ highWaterMark: 500 })
|
|
|
|
.on('data',function(data){
|
|
|
|
s.cx({
|
|
|
|
f: 'created_timelapse_file_chunk',
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
|
|
|
time: formattedTime,
|
|
|
|
filesize: e.filesize,
|
|
|
|
chunk: data,
|
2019-07-08 03:39:41 +00:00
|
|
|
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
2019-06-07 16:52:07 +00:00
|
|
|
filename: filename,
|
|
|
|
currentDate: currentDate,
|
|
|
|
queryInfo: queryInfo
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.on('close',function(){
|
|
|
|
s.cx({
|
|
|
|
f: 'created_timelapse_file',
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
|
|
|
time: formattedTime,
|
|
|
|
filesize: e.filesize,
|
2019-07-08 03:39:41 +00:00
|
|
|
d: s.group[e.ke].rawMonitorConfigurations[e.id],
|
2019-06-07 16:52:07 +00:00
|
|
|
filename: filename,
|
2019-06-07 21:30:33 +00:00
|
|
|
currentDate: currentDate,
|
2019-06-07 16:52:07 +00:00
|
|
|
queryInfo: queryInfo
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}else{
|
2019-07-08 03:06:46 +00:00
|
|
|
s.insertTimelapseFrameDatabaseRow(e,queryInfo,filePath)
|
2019-06-07 16:52:07 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-08 03:06:46 +00:00
|
|
|
s.insertTimelapseFrameDatabaseRow = function(e,queryInfo,filePath){
|
2019-06-07 16:52:07 +00:00
|
|
|
s.sqlQuery('INSERT INTO `Timelapse Frames` ('+Object.keys(queryInfo).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(queryInfo))
|
|
|
|
s.setDiskUsedForGroup(e,queryInfo.size / 1000000,'timelapeFrames')
|
|
|
|
s.purgeDiskForGroup(e)
|
|
|
|
s.onInsertTimelapseFrameExtensions.forEach(function(extender){
|
2019-07-08 03:06:46 +00:00
|
|
|
extender(e,queryInfo,filePath)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
s.onDeleteTimelapseFrameFromCloudExtensions = {}
|
|
|
|
s.onDeleteTimelapseFrameFromCloudExtensionsRunner = function(e,storageType,video){
|
|
|
|
// e = user
|
|
|
|
if(!storageType){
|
|
|
|
var videoDetails = JSON.parse(r.details)
|
|
|
|
videoDetails.type = videoDetails.type || 's3'
|
|
|
|
}
|
|
|
|
if(s.onDeleteTimelapseFrameFromCloudExtensions[storageType]){
|
|
|
|
s.onDeleteTimelapseFrameFromCloudExtensions[storageType](e,video,function(){
|
|
|
|
s.tx({
|
|
|
|
f: 'timelapse_frame_delete_cloud',
|
|
|
|
mid: e.mid,
|
|
|
|
ke: e.ke,
|
|
|
|
time: e.time,
|
|
|
|
end: e.end
|
|
|
|
},'GRP_'+e.ke);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.deleteTimelapseFrameFromCloud = function(e){
|
|
|
|
// e = video object
|
|
|
|
s.checkDetails(e)
|
|
|
|
var frameSelector = [e.id,e.ke,new Date(e.time)]
|
|
|
|
s.sqlQuery('SELECT * FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(err,r){
|
|
|
|
if(r&&r[0]){
|
|
|
|
r = r[0]
|
|
|
|
s.sqlQuery('DELETE FROM `Cloud Timelapse Frames` WHERE `mid`=? AND `ke`=? AND `time`=?',frameSelector,function(){
|
|
|
|
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,r)
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
// console.log('Delete Failed',e)
|
|
|
|
// console.error(err)
|
|
|
|
}
|
2019-06-07 16:52:07 +00:00
|
|
|
})
|
|
|
|
}
|
2019-07-10 06:12:17 +00:00
|
|
|
// Web Paths
|
|
|
|
// // // // //
|
|
|
|
/**
|
|
|
|
* API : Get Timelapse images
|
|
|
|
*/
|
|
|
|
app.get([
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapse/:ke',
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id',
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date',
|
|
|
|
], 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 `Timelapse Frames` 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var isMp4Call = false
|
|
|
|
if(req.query.mp4){
|
|
|
|
isMp4Call = true
|
|
|
|
}
|
|
|
|
if(req.params.date){
|
|
|
|
if(req.params.date.indexOf('-') === -1 && !isNaN(req.params.date)){
|
|
|
|
req.params.date = parseInt(req.params.date)
|
|
|
|
}
|
|
|
|
var selectedDate = req.params.date
|
|
|
|
if(typeof req.params.date === 'string' && req.params.date.indexOf('.') > -1){
|
|
|
|
isMp4Call = true
|
|
|
|
selectedDate = req.params.date.split('.')[0]
|
|
|
|
}
|
|
|
|
selectedDate = new Date(selectedDate)
|
|
|
|
var utcSelectedDate = new Date(selectedDate.getTime() + selectedDate.getTimezoneOffset() * 60000)
|
|
|
|
req.query.start = moment(utcSelectedDate).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
var dayAfter = utcSelectedDate
|
|
|
|
dayAfter.setDate(dayAfter.getDate() + 1)
|
|
|
|
req.query.end = moment(dayAfter).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
}
|
|
|
|
if(req.query.start||req.query.end){
|
|
|
|
if(!req.query.startOperator||req.query.startOperator==''){
|
|
|
|
req.query.startOperator='>='
|
|
|
|
}
|
|
|
|
if(!req.query.endOperator||req.query.endOperator==''){
|
|
|
|
req.query.endOperator='<='
|
|
|
|
}
|
|
|
|
if(req.query.start && req.query.start !== '' && req.query.end && req.query.end !== ''){
|
|
|
|
req.query.start = s.stringToSqlTime(req.query.start)
|
|
|
|
req.query.end = s.stringToSqlTime(req.query.end)
|
|
|
|
req.sql+=' AND `time` '+req.query.startOperator+' ? AND `time` '+req.query.endOperator+' ?';
|
|
|
|
req.ar.push(req.query.start)
|
|
|
|
req.ar.push(req.query.end)
|
|
|
|
}else if(req.query.start && req.query.start !== ''){
|
|
|
|
req.query.start = s.stringToSqlTime(req.query.start)
|
|
|
|
req.sql+=' AND `time` '+req.query.startOperator+' ?';
|
|
|
|
req.ar.push(req.query.start)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if(!req.query.limit||req.query.limit==''){req.query.limit=288}
|
|
|
|
req.sql+=' ORDER BY `time` DESC'
|
|
|
|
s.sqlQuery(req.sql,req.ar,function(err,r){
|
|
|
|
if(isMp4Call){
|
|
|
|
if(r && r[0]){
|
|
|
|
s.createVideoFromTimelapse(r,req.query.fps,function(response){
|
|
|
|
if(response.fileExists){
|
|
|
|
if(req.query.download){
|
|
|
|
res.setHeader('Content-Type', 'video/mp4')
|
|
|
|
s.streamMp4FileOverHttp(response.fileLocation,req,res)
|
|
|
|
}else{
|
|
|
|
res.setHeader('Content-Type', 'application/json')
|
|
|
|
res.end(s.prettyPrint({
|
|
|
|
ok : response.ok,
|
|
|
|
fileExists : response.fileExists,
|
|
|
|
msg : response.msg,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
res.setHeader('Content-Type', 'application/json')
|
|
|
|
res.end(s.prettyPrint({
|
|
|
|
ok : response.ok,
|
|
|
|
fileExists : response.fileExists,
|
|
|
|
msg : response.msg,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.end(s.prettyPrint([]))
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(r && r[0]){
|
|
|
|
r.forEach(function(file){
|
|
|
|
file.details = s.parseJSON(file.details)
|
|
|
|
})
|
|
|
|
res.end(s.prettyPrint(r))
|
|
|
|
}else{
|
|
|
|
res.end(s.prettyPrint([]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},res,req);
|
|
|
|
});
|
|
|
|
/**
|
|
|
|
* API : Get Timelapse images
|
|
|
|
*/
|
|
|
|
app.get([
|
|
|
|
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 `Timelapse Frames` 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 filename=?'
|
|
|
|
req.ar.push(req.params.filename)
|
|
|
|
req.sql+=' ORDER BY `time` DESC'
|
|
|
|
s.sqlQuery(req.sql,req.ar,function(err,r){
|
|
|
|
if(r && r[0]){
|
|
|
|
var frame = r[0]
|
|
|
|
frame.details = s.parseJSON(frame.details)
|
|
|
|
var fileLocation
|
|
|
|
if(frame.details.dir){
|
|
|
|
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
|
|
|
|
}else{
|
|
|
|
fileLocation = `${s.dir.videos}`
|
|
|
|
}
|
|
|
|
var selectedDate = req.params.date
|
|
|
|
if(selectedDate.indexOf('-') === -1){
|
|
|
|
selectedDate = req.params.filename.split('T')[0]
|
|
|
|
}
|
|
|
|
fileLocation = `${fileLocation}${frame.ke}/${frame.mid}_timelapse/${selectedDate}/${req.params.filename}`
|
|
|
|
if(fs.existsSync(fileLocation)){
|
|
|
|
res.contentType('image/jpeg')
|
|
|
|
res.on('finish',function(){res.end()})
|
|
|
|
fs.createReadStream(fileLocation).pipe(res)
|
|
|
|
}else{
|
|
|
|
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
res.end(s.prettyPrint({ok: false, msg: lang[`Nothing exists`]}))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},res,req);
|
|
|
|
});
|
|
|
|
/**
|
|
|
|
* Page : Get Timelapse Page (Not Modal)
|
|
|
|
*/
|
|
|
|
app.get(config.webPaths.apiPrefix+':auth/timelapsePage/:ke', function (req,res){
|
|
|
|
req.params.protocol=req.protocol;
|
|
|
|
s.auth(req.params,function(user){
|
|
|
|
// if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
|
|
|
|
// res.end(user.lang['Not Permitted'])
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
req.params.uid = user.uid
|
|
|
|
s.renderPage(req,res,config.renderPaths.timelapse,{
|
|
|
|
$user: user,
|
|
|
|
data: req.params,
|
|
|
|
config: s.getConfigWithBranding(req.hostname),
|
|
|
|
lang: user.lang,
|
|
|
|
originalURL: s.getOriginalUrl(req)
|
|
|
|
})
|
|
|
|
},res,req);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Auto Build Timelapse Videos
|
|
|
|
if(config.autoBuildTimelapseVideosDaily === true){
|
|
|
|
setInterval(function(){
|
|
|
|
var dateNow = new Date()
|
|
|
|
var hoursNow = dateNow.getHours()
|
|
|
|
if(hoursNow === 1){
|
2019-07-26 15:18:24 +00:00
|
|
|
var dateNowMoment = moment(dateNow).utc().format('YYYY-MM-DDTHH:mm:ss')
|
|
|
|
var dateMinusOneDay = moment(dateNow).utc().subtract(1, 'days').format('YYYY-MM-DDTHH:mm:ss')
|
2019-07-10 06:12:17 +00:00
|
|
|
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE time => ? AND time =< ?',[dateMinusOneDay,dateNowMoment],function(err,frames){
|
|
|
|
console.log(frames.length)
|
|
|
|
var groups = {}
|
|
|
|
frames.forEach(function(frame){
|
|
|
|
if(groups[frame.ke])groups[frame.ke] = {}
|
|
|
|
if(groups[frame.ke][frame.mid])groups[frame.ke][frame.mid] = []
|
|
|
|
groups[frame.ke][frame.mid].push(frame)
|
|
|
|
})
|
|
|
|
Object.keys(groups).forEach(function(groupKey){
|
|
|
|
Object.keys(groups[groupKey]).forEach(function(monitorId){
|
|
|
|
var frameSet = groups[groupKey][monitorId]
|
|
|
|
s.createVideoFromTimelapse(frameSet,30,function(response){
|
|
|
|
if(response.ok){
|
|
|
|
|
|
|
|
}
|
|
|
|
console.log(response.fileLocation)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},1000 * 60 * 60 * 0.75)//every 45 minutes
|
|
|
|
}
|
2019-06-07 16:52:07 +00:00
|
|
|
}
|