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){
|
2021-11-27 01:47:20 +00:00
|
|
|
const {
|
|
|
|
sendTimelapseFrameToMasterNode,
|
|
|
|
} = require('./childNode/childUtils.js')(s,config,lang)
|
2020-10-10 05:49:11 +00:00
|
|
|
const timelapseFramesCache = {}
|
|
|
|
const timelapseFramesCacheTimeouts = {}
|
2019-06-07 16:52:07 +00:00
|
|
|
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/';
|
|
|
|
}
|
|
|
|
}
|
2021-05-20 03:18:19 +00:00
|
|
|
s.createTimelapseFrameAndInsert = function(e,location,filename,eventTime,frameDetails){
|
2019-06-07 16:52:07 +00:00
|
|
|
//e = monitor object
|
|
|
|
//location = file location
|
|
|
|
var filePath = location + filename
|
|
|
|
var fileStats = fs.statSync(filePath)
|
2021-05-20 03:18:19 +00:00
|
|
|
var details = Object.assign({},frameDetails || {})
|
2019-06-07 16:52:07 +00:00
|
|
|
if(e.details && e.details.dir && e.details.dir !== ''){
|
|
|
|
details.dir = e.details.dir
|
|
|
|
}
|
2021-11-27 01:47:20 +00:00
|
|
|
const timeNow = eventTime || new Date()
|
|
|
|
const queryInfo = {
|
2019-06-07 16:52:07 +00:00
|
|
|
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){
|
2021-11-27 01:47:20 +00:00
|
|
|
var currentDate = s.formattedTime(timeNow,'YYYY-MM-DD')
|
|
|
|
const childNodeData = {
|
2019-06-07 16:52:07 +00:00
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
2021-12-09 23:29:48 +00:00
|
|
|
time: currentDate,
|
2019-06-07 16:52:07 +00:00
|
|
|
filename: filename,
|
|
|
|
currentDate: currentDate,
|
|
|
|
queryInfo: queryInfo
|
2021-11-27 01:47:20 +00:00
|
|
|
}
|
|
|
|
sendTimelapseFrameToMasterNode(filePath,childNodeData)
|
2019-06-07 16:52:07 +00:00
|
|
|
}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){
|
2020-07-11 00:30:08 +00:00
|
|
|
s.knexQuery({
|
|
|
|
action: "insert",
|
|
|
|
table: "Timelapse Frames",
|
|
|
|
insert: queryInfo
|
|
|
|
})
|
2020-08-07 04:33:58 +00:00
|
|
|
s.setDiskUsedForGroup(e.ke,queryInfo.size / 1048576,'timelapeFrames')
|
2020-08-07 07:54:56 +00:00
|
|
|
s.purgeDiskForGroup(e.ke)
|
2019-06-07 16:52:07 +00:00
|
|
|
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)
|
2020-08-07 23:37:27 +00:00
|
|
|
var frameSelector = {
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.id,
|
|
|
|
time: new Date(e.time),
|
|
|
|
}
|
2020-07-11 00:30:08 +00:00
|
|
|
s.knexQuery({
|
|
|
|
action: "select",
|
|
|
|
columns: "*",
|
|
|
|
table: "Cloud Timelapse Frames",
|
2020-08-06 01:15:10 +00:00
|
|
|
where: frameSelector,
|
|
|
|
limit: 1
|
2020-07-11 00:30:08 +00:00
|
|
|
},function(err,r){
|
|
|
|
if(r && r[0]){
|
2019-07-08 03:06:46 +00:00
|
|
|
r = r[0]
|
2020-07-11 00:30:08 +00:00
|
|
|
s.knexQuery({
|
|
|
|
action: "delete",
|
|
|
|
table: "Cloud Timelapse Frames",
|
2020-08-06 01:15:10 +00:00
|
|
|
where: frameSelector,
|
|
|
|
limit: 1
|
2020-07-11 00:30:08 +00:00
|
|
|
},function(){
|
2019-07-08 03:06:46 +00:00
|
|
|
s.onDeleteTimelapseFrameFromCloudExtensionsRunner(e,r)
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
// console.log('Delete Failed',e)
|
2020-10-10 05:49:11 +00:00
|
|
|
// console.error(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
const deleteTimelapseFrame = function(e){
|
|
|
|
// e = video object
|
|
|
|
s.checkDetails(e)
|
|
|
|
var frameSelector = {
|
|
|
|
ke: e.ke,
|
|
|
|
mid: e.mid,
|
|
|
|
filename: e.filename,
|
|
|
|
}
|
|
|
|
s.knexQuery({
|
|
|
|
action: "select",
|
|
|
|
columns: "*",
|
|
|
|
table: "Timelapse Frames",
|
|
|
|
where: frameSelector,
|
|
|
|
limit: 1
|
|
|
|
},function(err,r){
|
|
|
|
if(r && r[0]){
|
|
|
|
r = r[0]
|
|
|
|
s.knexQuery({
|
|
|
|
action: "delete",
|
|
|
|
table: "Timelapse Frames",
|
|
|
|
where: frameSelector,
|
|
|
|
limit: 1
|
|
|
|
},function(){
|
|
|
|
s.file('delete',e.fileLocation)
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
// console.log('Delete Failed',e)
|
2019-07-08 03:06:46 +00:00
|
|
|
// 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" ||
|
2020-07-11 00:30:08 +00:00
|
|
|
hasRestrictions &&
|
|
|
|
(
|
|
|
|
!user.details.video_view ||
|
|
|
|
user.details.video_view.indexOf(req.params.id) === -1
|
|
|
|
)
|
2019-07-10 06:12:17 +00:00
|
|
|
){
|
2020-07-11 00:30:08 +00:00
|
|
|
s.closeJsonResponse(res,[])
|
2019-07-10 06:12:17 +00:00
|
|
|
return
|
|
|
|
}
|
2020-07-11 00:30:08 +00:00
|
|
|
const monitorRestrictions = s.getMonitorRestrictions(user.details,req.params.id)
|
2020-07-11 21:11:39 +00:00
|
|
|
s.getDatabaseRows({
|
2020-07-11 00:30:08 +00:00
|
|
|
monitorRestrictions: monitorRestrictions,
|
2020-07-11 21:11:39 +00:00
|
|
|
table: 'Timelapse Frames',
|
2020-07-11 00:30:08 +00:00
|
|
|
groupKey: req.params.ke,
|
|
|
|
date: req.query.date,
|
2020-07-11 21:11:39 +00:00
|
|
|
startDate: req.query.start,
|
|
|
|
endDate: req.query.end,
|
2020-07-11 00:30:08 +00:00
|
|
|
startOperator: req.query.startOperator,
|
|
|
|
endOperator: req.query.endOperator,
|
2021-12-03 18:20:56 +00:00
|
|
|
noLimit: req.query.noLimit,
|
2020-07-11 00:30:08 +00:00
|
|
|
limit: req.query.limit,
|
|
|
|
archived: req.query.archived,
|
2020-07-28 01:12:57 +00:00
|
|
|
rowType: 'frames',
|
|
|
|
endIsStartTo: true
|
2020-07-11 00:30:08 +00:00
|
|
|
},(response) => {
|
|
|
|
var isMp4Call = !!(req.query.mp4 || (req.params.date && typeof req.params.date === 'string' && req.params.date.indexOf('.') > -1))
|
|
|
|
if(isMp4Call && response.frames[0]){
|
|
|
|
s.createVideoFromTimelapse(response.frames,req.query.fps,function(response){
|
|
|
|
if(response.fileExists){
|
|
|
|
if(req.query.download){
|
|
|
|
res.setHeader('Content-Type', 'video/mp4')
|
|
|
|
s.streamMp4FileOverHttp(response.fileLocation,req,res)
|
2019-07-10 06:12:17 +00:00
|
|
|
}else{
|
2020-07-11 00:30:08 +00:00
|
|
|
s.closeJsonResponse(res,{
|
2019-07-10 06:12:17 +00:00
|
|
|
ok : response.ok,
|
|
|
|
fileExists : response.fileExists,
|
|
|
|
msg : response.msg,
|
2020-07-11 00:30:08 +00:00
|
|
|
})
|
2019-07-10 06:12:17 +00:00
|
|
|
}
|
2020-07-11 00:30:08 +00:00
|
|
|
}else{
|
|
|
|
s.closeJsonResponse(res,{
|
|
|
|
ok : response.ok,
|
|
|
|
fileExists : response.fileExists,
|
|
|
|
msg : response.msg,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2019-07-10 06:12:17 +00:00
|
|
|
}else{
|
2020-07-11 00:30:08 +00:00
|
|
|
s.closeJsonResponse(res,response.frames)
|
2019-07-10 06:12:17 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
},res,req);
|
|
|
|
});
|
|
|
|
/**
|
2020-10-09 19:34:15 +00:00
|
|
|
* API : Build MP4 File
|
|
|
|
*/
|
|
|
|
app.post([
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapseBuildVideo/:ke',
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapseBuildVideo/: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
|
|
|
|
)
|
|
|
|
){
|
|
|
|
s.closeJsonResponse(res,[])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const monitorRestrictions = s.getMonitorRestrictions(user.details,req.params.id)
|
|
|
|
if(monitorRestrictions.length === 0){
|
|
|
|
s.closeJsonResponse(res,{
|
|
|
|
ok: false
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const framesPosted = s.getPostData(req, 'frames', true) || []
|
|
|
|
const frames = []
|
|
|
|
var n = 0
|
|
|
|
framesPosted.forEach((frame) => {
|
2021-12-07 04:25:55 +00:00
|
|
|
var firstParam = [['ke','=',req.params.ke],['mid','=',req.params.id],['filename','=',frame.filename]]
|
|
|
|
if(n !== 0)firstParam[0] = (['or']).concat(firstParam[0])
|
|
|
|
frames.push(...firstParam)
|
2020-10-09 19:34:15 +00:00
|
|
|
++n
|
|
|
|
})
|
|
|
|
s.knexQuery({
|
|
|
|
action: "select",
|
|
|
|
columns: "*",
|
|
|
|
table: "Timelapse Frames",
|
|
|
|
where: frames
|
|
|
|
},(err,r) => {
|
2021-12-07 04:25:55 +00:00
|
|
|
s.debugLog("Timelapse Frames Building Video",r.length)
|
2020-10-09 19:34:15 +00:00
|
|
|
if(r.length === 0){
|
|
|
|
s.closeJsonResponse(res,{
|
|
|
|
ok: false
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
2020-10-09 19:47:08 +00:00
|
|
|
s.createVideoFromTimelapse(r.reverse(),s.getPostData(req, 'fps'),function(response){
|
2020-10-09 19:34:15 +00:00
|
|
|
s.closeJsonResponse(res,{
|
|
|
|
ok : response.ok,
|
|
|
|
filename : response.filename,
|
|
|
|
fileExists : response.fileExists,
|
|
|
|
msg : response.msg,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},res,req);
|
|
|
|
});
|
|
|
|
/**
|
2019-07-10 06:12:17 +00:00
|
|
|
* API : Get Timelapse images
|
|
|
|
*/
|
|
|
|
app.get([
|
|
|
|
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date/:filename',
|
2020-10-10 05:49:11 +00:00
|
|
|
config.webPaths.apiPrefix+':auth/timelapse/:ke/:id/:date/:filename/:action',
|
2019-07-10 06:12:17 +00:00
|
|
|
], 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
|
|
|
|
}
|
2020-07-11 00:30:08 +00:00
|
|
|
const monitorRestrictions = s.getMonitorRestrictions(user.details,req.params.id)
|
2020-10-10 05:49:11 +00:00
|
|
|
const cacheKey = req.params.ke + req.params.id + req.params.filename
|
|
|
|
const processFrame = (frame) => {
|
|
|
|
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}`
|
2021-02-11 17:45:05 +00:00
|
|
|
if(req.params.action === 'delete'){
|
|
|
|
deleteTimelapseFrame({
|
|
|
|
ke: frame.ke,
|
|
|
|
mid: frame.mid,
|
|
|
|
filename: req.params.filename,
|
|
|
|
fileLocation: fileLocation,
|
|
|
|
})
|
|
|
|
delete(timelapseFramesCache[cacheKey])
|
|
|
|
s.closeJsonResponse(res,{ok: true})
|
|
|
|
}else{
|
|
|
|
fs.stat(fileLocation,function(err,stats){
|
|
|
|
if(!err){
|
2019-09-08 06:17:57 +00:00
|
|
|
res.contentType('image/jpeg')
|
|
|
|
res.on('finish',function(){res.end()})
|
|
|
|
fs.createReadStream(fileLocation).pipe(res)
|
2021-02-11 17:45:05 +00:00
|
|
|
}else{
|
|
|
|
s.closeJsonResponse(res,{ok: false, msg: lang[`Nothing exists`]})
|
2019-09-08 06:17:57 +00:00
|
|
|
}
|
2021-05-20 03:18:19 +00:00
|
|
|
})
|
2021-02-11 17:45:05 +00:00
|
|
|
}
|
2020-10-10 05:49:11 +00:00
|
|
|
}
|
|
|
|
if(timelapseFramesCache[cacheKey]){
|
|
|
|
processFrame(timelapseFramesCache[cacheKey])
|
|
|
|
}else{
|
|
|
|
s.getDatabaseRows({
|
|
|
|
monitorRestrictions: monitorRestrictions,
|
|
|
|
table: 'Timelapse Frames',
|
|
|
|
groupKey: req.params.ke,
|
|
|
|
archived: req.query.archived,
|
|
|
|
filename: req.params.filename,
|
2021-12-07 06:35:19 +00:00
|
|
|
limit: 1,
|
2020-10-10 05:49:11 +00:00
|
|
|
rowType: 'frames',
|
|
|
|
endIsStartTo: true
|
|
|
|
},(response) => {
|
|
|
|
var frame = response.frames[0]
|
|
|
|
if(frame){
|
|
|
|
timelapseFramesCache[cacheKey] = frame
|
|
|
|
timelapseFramesCacheTimeouts[cacheKey] = setTimeout(function(){
|
|
|
|
delete(timelapseFramesCache[cacheKey])
|
|
|
|
},1000 * 60 * 10)
|
|
|
|
processFrame(frame)
|
|
|
|
}else{
|
|
|
|
s.closeJsonResponse(res,{ok: false, msg: lang[`Nothing exists`]})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2019-07-10 06:12:17 +00:00
|
|
|
},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);
|
|
|
|
});
|
2019-07-30 14:31:34 +00:00
|
|
|
var buildTimelapseVideos = function(){
|
|
|
|
var dateNow = new Date()
|
|
|
|
var hoursNow = dateNow.getHours()
|
|
|
|
if(hoursNow === 1){
|
|
|
|
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')
|
2020-07-11 00:30:08 +00:00
|
|
|
s.knexQuery({
|
|
|
|
action: "select",
|
|
|
|
columns: "*",
|
|
|
|
table: "Timelapse Frames",
|
|
|
|
where: [
|
|
|
|
['time','=>',dateMinusOneDay],
|
|
|
|
['time','=<',dateNowMoment],
|
|
|
|
]
|
|
|
|
},function(err,frames) {
|
2019-07-30 14:31:34 +00:00
|
|
|
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){
|
2019-07-10 06:12:17 +00:00
|
|
|
|
2019-07-30 14:31:34 +00:00
|
|
|
}
|
2019-07-10 06:12:17 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2019-07-30 14:31:34 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Auto Build Timelapse Videos
|
|
|
|
if(config.autoBuildTimelapseVideosDaily === true){
|
|
|
|
setInterval(buildTimelapseVideos,1000 * 60 * 60 * 0.75)//every 45 minutes
|
|
|
|
buildTimelapseVideos()
|
2019-07-10 06:12:17 +00:00
|
|
|
}
|
2019-06-07 16:52:07 +00:00
|
|
|
}
|