2018-09-28 05:37:08 +00:00
|
|
|
var fs = require('fs');
|
|
|
|
var exec = require('child_process').exec;
|
|
|
|
var spawn = require('child_process').spawn;
|
2018-09-29 20:40:07 +00:00
|
|
|
module.exports = function(s,config,lang){
|
2018-10-12 00:36:27 +00:00
|
|
|
/**
|
|
|
|
* Gets the video directory of the supplied video or monitor database row.
|
|
|
|
* @constructor
|
|
|
|
* @param {object} e - Monitor object or Video object. Object is a database row.
|
|
|
|
*/
|
2018-09-29 15:00:51 +00:00
|
|
|
s.getVideoDirectory = function(e){
|
|
|
|
if(e.mid&&!e.id){e.id=e.mid};
|
2018-10-08 23:54:53 +00:00
|
|
|
s.checkDetails(e)
|
2018-09-29 15:00:51 +00:00
|
|
|
if(e.details&&e.details.dir&&e.details.dir!==''){
|
|
|
|
return s.checkCorrectPathEnding(e.details.dir)+e.ke+'/'+e.id+'/'
|
|
|
|
}else{
|
2019-04-26 18:10:53 +00:00
|
|
|
return s.dir.videos+e.ke+'/'+e.id+'/';
|
|
|
|
}
|
|
|
|
}
|
2018-10-12 00:36:27 +00:00
|
|
|
/**
|
|
|
|
* Creates available API based URLs for streaming
|
|
|
|
* @constructor
|
|
|
|
* @param {object} videos - Array of video objects
|
|
|
|
* @param {object} options - Contains middle parameter of URL and auth key
|
|
|
|
* @param [options.auth] {string} - API Key
|
|
|
|
* @param [options.videoParam] {string} - currently only `videos` and `cloudVideos` available.
|
|
|
|
*/
|
2018-09-29 15:00:51 +00:00
|
|
|
s.buildVideoLinks = function(videos,options){
|
|
|
|
videos.forEach(function(v){
|
2019-08-14 17:42:41 +00:00
|
|
|
var details = s.parseJSON(v.details)
|
2018-09-29 15:00:51 +00:00
|
|
|
var queryString = []
|
|
|
|
if(details.isUTC === true){
|
|
|
|
queryString.push('isUTC=true')
|
|
|
|
}else{
|
|
|
|
v.time = s.utcToLocal(v.time)
|
|
|
|
v.end = s.utcToLocal(v.end)
|
|
|
|
}
|
|
|
|
if(queryString.length > 0){
|
|
|
|
queryString = '?'+queryString.join('&')
|
|
|
|
}else{
|
|
|
|
queryString = ''
|
|
|
|
}
|
|
|
|
if(!v.ext && v.href){
|
|
|
|
v.ext = v.href.split('.')
|
|
|
|
v.ext = v.ext[v.ext.length - 1]
|
|
|
|
}
|
|
|
|
v.filename = s.formattedTime(v.time)+'.'+v.ext;
|
|
|
|
if(!options.videoParam)options.videoParam = 'videos'
|
2019-06-07 23:55:25 +00:00
|
|
|
var href = s.checkCorrectPathEnding(config.webPaths.apiPrefix) + options.auth+'/'+options.videoParam+'/'+v.ke+'/'+v.mid+'/'+v.filename;
|
2018-09-29 15:00:51 +00:00
|
|
|
v.actionUrl = href
|
|
|
|
v.links = {
|
|
|
|
deleteVideo : href+'/delete' + queryString,
|
|
|
|
changeToUnread : href+'/status/1' + queryString,
|
|
|
|
changeToRead : href+'/status/2' + queryString
|
|
|
|
}
|
|
|
|
if(!v.href || options.hideRemote === true)v.href = href + queryString
|
|
|
|
v.details = details
|
|
|
|
})
|
|
|
|
}
|
2018-10-18 20:41:20 +00:00
|
|
|
s.insertDatabaseRow = function(e,k,callback){
|
|
|
|
s.checkDetails(e)
|
|
|
|
//save database row
|
2019-10-04 15:16:57 +00:00
|
|
|
if(!k.details)k.details = {}
|
2019-08-14 17:42:41 +00:00
|
|
|
if(e.details && e.details.dir && e.details.dir !== ''){
|
2018-10-18 20:41:20 +00:00
|
|
|
k.details.dir = e.details.dir
|
|
|
|
}
|
|
|
|
if(config.useUTC === true)k.details.isUTC = config.useUTC;
|
|
|
|
var save = [
|
|
|
|
e.mid,
|
|
|
|
e.ke,
|
|
|
|
k.startTime,
|
|
|
|
e.ext,
|
|
|
|
1,
|
|
|
|
s.s(k.details),
|
|
|
|
k.filesize,
|
|
|
|
k.endTime,
|
|
|
|
]
|
|
|
|
s.sqlQuery('INSERT INTO Videos (mid,ke,time,ext,status,details,size,end) VALUES (?,?,?,?,?,?,?,?)',save,function(err){
|
|
|
|
if(callback)callback(err)
|
|
|
|
fs.chmod(k.dir+k.file,0o777,function(err){
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2018-09-30 22:44:04 +00:00
|
|
|
//on video completion
|
2018-10-05 22:49:44 +00:00
|
|
|
s.insertCompletedVideo = function(e,k,callback){
|
2018-10-10 06:56:13 +00:00
|
|
|
//e = monitor object
|
|
|
|
//k = video insertion object
|
2018-10-08 23:54:53 +00:00
|
|
|
s.checkDetails(e)
|
2018-09-28 05:37:08 +00:00
|
|
|
if(!k)k={};
|
2018-09-29 15:00:51 +00:00
|
|
|
e.dir = s.getVideoDirectory(e)
|
|
|
|
k.dir = e.dir.toString()
|
2019-07-08 03:39:41 +00:00
|
|
|
if(s.group[e.ke].activeMonitors[e.id].childNode){
|
|
|
|
s.cx({f:'insertCompleted',d:s.group[e.ke].rawMonitorConfigurations[e.id],k:k},s.group[e.ke].activeMonitors[e.id].childNodeId);
|
2018-09-29 15:00:51 +00:00
|
|
|
}else{
|
|
|
|
//get file directory
|
|
|
|
k.fileExists = fs.existsSync(k.dir+k.file)
|
|
|
|
if(k.fileExists!==true){
|
|
|
|
k.dir = s.dir.videos+'/'+e.ke+'/'+e.id+'/'
|
|
|
|
k.fileExists = fs.existsSync(k.dir+k.file)
|
|
|
|
if(k.fileExists !== true){
|
|
|
|
s.dir.addStorage.forEach(function(v){
|
|
|
|
if(k.fileExists !== true){
|
|
|
|
k.dir = s.checkCorrectPathEnding(v.path)+e.ke+'/'+e.id+'/'
|
|
|
|
k.fileExists = fs.existsSync(k.dir+k.file)
|
|
|
|
}
|
|
|
|
})
|
2018-09-28 05:37:08 +00:00
|
|
|
}
|
2018-09-29 15:00:51 +00:00
|
|
|
}
|
|
|
|
if(k.fileExists===true){
|
|
|
|
//close video row
|
2019-08-14 17:42:41 +00:00
|
|
|
k.details = {}
|
2018-09-29 15:00:51 +00:00
|
|
|
k.stat = fs.statSync(k.dir+k.file)
|
|
|
|
k.filesize = k.stat.size
|
2020-02-01 02:30:24 +00:00
|
|
|
k.filesizeMB = parseFloat((k.filesize/1048576).toFixed(2))
|
2018-09-29 15:00:51 +00:00
|
|
|
|
|
|
|
k.startTime = new Date(s.nameToTime(k.file))
|
2019-11-04 16:47:20 +00:00
|
|
|
k.endTime = new Date(k.endTime || k.stat.mtime)
|
2018-09-29 15:00:51 +00:00
|
|
|
if(config.useUTC === true){
|
|
|
|
fs.rename(k.dir+k.file, k.dir+s.formattedTime(k.startTime)+'.'+e.ext, (err) => {
|
|
|
|
if (err) return console.error(err);
|
|
|
|
});
|
|
|
|
k.filename = s.formattedTime(k.startTime)+'.'+e.ext
|
2018-09-28 05:37:08 +00:00
|
|
|
}else{
|
2018-09-29 15:00:51 +00:00
|
|
|
k.filename = k.file
|
2018-09-28 05:37:08 +00:00
|
|
|
}
|
2018-09-29 15:00:51 +00:00
|
|
|
if(!e.ext){e.ext = k.filename.split('.')[1]}
|
|
|
|
//send event for completed recording
|
|
|
|
if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){
|
2018-10-19 21:15:40 +00:00
|
|
|
fs.createReadStream(k.dir+k.filename,{ highWaterMark: 500 })
|
2018-09-29 15:00:51 +00:00
|
|
|
.on('data',function(data){
|
|
|
|
s.cx({
|
|
|
|
f:'created_file_chunk',
|
2018-10-19 21:15:40 +00:00
|
|
|
mid:e.mid,
|
2018-09-29 15:00:51 +00:00
|
|
|
ke:e.ke,
|
|
|
|
chunk:data,
|
|
|
|
filename:k.filename,
|
|
|
|
d:s.cleanMonitorObject(e),
|
|
|
|
filesize:e.filesize,
|
|
|
|
time:s.timeObject(k.startTime).format(),
|
|
|
|
end:s.timeObject(k.endTime).format()
|
2018-09-28 05:37:08 +00:00
|
|
|
})
|
2018-09-29 15:00:51 +00:00
|
|
|
})
|
|
|
|
.on('close',function(){
|
2019-07-08 03:39:41 +00:00
|
|
|
clearTimeout(s.group[e.ke].activeMonitors[e.id].recordingChecker)
|
|
|
|
clearTimeout(s.group[e.ke].activeMonitors[e.id].streamChecker)
|
2018-09-29 15:00:51 +00:00
|
|
|
s.cx({
|
|
|
|
f:'created_file',
|
|
|
|
mid:e.id,
|
|
|
|
ke:e.ke,
|
|
|
|
filename:k.filename,
|
|
|
|
d:s.cleanMonitorObject(e),
|
|
|
|
filesize:k.filesize,
|
|
|
|
time:s.timeObject(k.startTime).format(),
|
|
|
|
end:s.timeObject(k.endTime).format()
|
|
|
|
})
|
2018-09-29 20:40:07 +00:00
|
|
|
})
|
2018-09-28 05:37:08 +00:00
|
|
|
}else{
|
2018-09-29 15:00:51 +00:00
|
|
|
var href = '/videos/'+e.ke+'/'+e.mid+'/'+k.filename
|
|
|
|
if(config.useUTC === true)href += '?isUTC=true';
|
|
|
|
s.txWithSubPermissions({
|
|
|
|
f:'video_build_success',
|
|
|
|
hrefNoAuth:href,
|
|
|
|
filename:k.filename,
|
|
|
|
mid:e.mid,
|
|
|
|
ke:e.ke,
|
|
|
|
time:k.startTime,
|
|
|
|
size:k.filesize,
|
|
|
|
end:k.endTime
|
2018-10-02 00:21:48 +00:00
|
|
|
},'GRP_'+e.ke,'video_view')
|
2018-10-18 04:42:32 +00:00
|
|
|
//purge over max
|
|
|
|
s.purgeDiskForGroup(e)
|
|
|
|
//send new diskUsage values
|
2019-04-19 00:54:07 +00:00
|
|
|
var storageIndex = s.getVideoStorageIndex(e)
|
|
|
|
if(storageIndex){
|
|
|
|
s.setDiskUsedForGroupAddStorage(e,{
|
|
|
|
size: k.filesizeMB,
|
|
|
|
storageIndex: storageIndex
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
s.setDiskUsedForGroup(e,k.filesizeMB)
|
|
|
|
}
|
2019-08-14 17:42:41 +00:00
|
|
|
s.onBeforeInsertCompletedVideoExtensions.forEach(function(extender){
|
|
|
|
extender(e,k)
|
|
|
|
})
|
2018-10-18 20:41:20 +00:00
|
|
|
s.insertDatabaseRow(e,k,callback)
|
2018-10-19 21:15:40 +00:00
|
|
|
s.insertCompletedVideoExtensions.forEach(function(extender){
|
|
|
|
extender(e,k)
|
|
|
|
})
|
2018-09-29 15:00:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.deleteVideo = function(e){
|
2018-09-29 23:03:55 +00:00
|
|
|
//e = video object
|
2018-10-08 23:54:53 +00:00
|
|
|
s.checkDetails(e)
|
2018-09-29 15:00:51 +00:00
|
|
|
e.dir = s.getVideoDirectory(e)
|
|
|
|
if(!e.filename && e.time){
|
|
|
|
e.filename = s.formattedTime(e.time)
|
|
|
|
}
|
|
|
|
var filename,
|
|
|
|
time
|
|
|
|
if(e.filename.indexOf('.')>-1){
|
|
|
|
filename = e.filename
|
|
|
|
}else{
|
|
|
|
filename = e.filename+'.'+e.ext
|
2018-09-28 05:37:08 +00:00
|
|
|
}
|
2018-09-29 15:00:51 +00:00
|
|
|
if(e.filename && !e.time){
|
|
|
|
time = s.nameToTime(filename)
|
|
|
|
}else{
|
|
|
|
time = e.time
|
|
|
|
}
|
|
|
|
time = new Date(time)
|
2018-10-08 21:43:31 +00:00
|
|
|
var queryValues = [e.id,e.ke,time];
|
|
|
|
s.sqlQuery('SELECT * FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',queryValues,function(err,r){
|
2018-10-05 21:17:10 +00:00
|
|
|
if(r && r[0]){
|
|
|
|
r = r[0]
|
2018-10-08 22:15:31 +00:00
|
|
|
fs.chmod(e.dir+filename,0o777,function(err){
|
2018-10-08 23:54:53 +00:00
|
|
|
s.tx({
|
|
|
|
f: 'video_delete',
|
|
|
|
filename: filename,
|
|
|
|
mid: e.id,
|
|
|
|
ke: e.ke,
|
|
|
|
time: s.nameToTime(filename),
|
|
|
|
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
|
|
|
},'GRP_'+e.ke);
|
2019-04-19 00:54:07 +00:00
|
|
|
var storageIndex = s.getVideoStorageIndex(e)
|
|
|
|
if(storageIndex){
|
|
|
|
s.setDiskUsedForGroupAddStorage(e,{
|
2020-02-01 02:30:24 +00:00
|
|
|
size: -(r.size / 1048576),
|
2019-04-19 00:54:07 +00:00
|
|
|
storageIndex: storageIndex
|
|
|
|
})
|
|
|
|
}else{
|
2020-02-01 02:30:24 +00:00
|
|
|
s.setDiskUsedForGroup(e,-(r.size / 1048576))
|
2019-04-19 00:54:07 +00:00
|
|
|
}
|
2018-10-08 23:54:53 +00:00
|
|
|
s.sqlQuery('DELETE FROM Videos WHERE `mid`=? AND `ke`=? AND `time`=?',queryValues,function(err){
|
2018-09-29 15:00:51 +00:00
|
|
|
if(err){
|
2018-10-08 21:43:31 +00:00
|
|
|
s.systemLog(lang['File Delete Error'] + ' : '+e.ke+' : '+' : '+e.id,err)
|
2018-09-29 15:00:51 +00:00
|
|
|
}
|
|
|
|
})
|
2018-10-09 04:44:36 +00:00
|
|
|
fs.unlink(e.dir+filename,function(err){
|
|
|
|
fs.stat(e.dir+filename,function(err){
|
|
|
|
if(!err){
|
|
|
|
s.file('delete',e.dir+filename)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2018-09-29 15:00:51 +00:00
|
|
|
})
|
|
|
|
}else{
|
2018-11-05 20:55:58 +00:00
|
|
|
console.log(new Error())
|
2018-10-08 21:43:31 +00:00
|
|
|
console.log(lang['Database row does not exist'],queryValues)
|
2018-09-29 15:00:51 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2018-11-05 20:55:58 +00:00
|
|
|
s.deleteListOfVideos = function(videos){
|
2019-02-17 19:49:37 +00:00
|
|
|
var deleteSetOfVideos = function(videos){
|
|
|
|
var query = 'DELETE FROM Videos WHERE '
|
|
|
|
var videoQuery = []
|
|
|
|
var queryValues = []
|
|
|
|
videos.forEach(function(video){
|
|
|
|
s.checkDetails(video)
|
|
|
|
//e = video object
|
|
|
|
video.dir = s.getVideoDirectory(video)
|
|
|
|
if(!video.filename && video.time){
|
|
|
|
video.filename = s.formattedTime(video.time)
|
|
|
|
}
|
|
|
|
var filename,
|
|
|
|
time
|
|
|
|
if(video.filename.indexOf('.')>-1){
|
|
|
|
filename = video.filename
|
|
|
|
}else{
|
|
|
|
filename = video.filename+'.'+video.ext
|
|
|
|
}
|
|
|
|
if(video.filename && !video.time){
|
|
|
|
time = s.nameToTime(filename)
|
|
|
|
}else{
|
|
|
|
time = video.time
|
|
|
|
}
|
|
|
|
time = new Date(time)
|
|
|
|
fs.chmod(video.dir+filename,0o777,function(err){
|
|
|
|
s.tx({
|
|
|
|
f: 'video_delete',
|
|
|
|
filename: filename,
|
|
|
|
mid: video.id,
|
|
|
|
ke: video.ke,
|
|
|
|
time: s.nameToTime(filename),
|
|
|
|
end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
|
|
|
|
},'GRP_'+video.ke);
|
2019-04-19 00:54:07 +00:00
|
|
|
var storageIndex = s.getVideoStorageIndex(video)
|
|
|
|
if(storageIndex){
|
|
|
|
s.setDiskUsedForGroupAddStorage(video,{
|
2020-02-01 02:30:24 +00:00
|
|
|
size: -(video.size / 1048576),
|
2019-04-19 00:54:07 +00:00
|
|
|
storageIndex: storageIndex
|
|
|
|
})
|
|
|
|
}else{
|
2020-02-01 02:30:24 +00:00
|
|
|
s.setDiskUsedForGroup(video,-(video.size / 1048576))
|
2019-04-19 00:54:07 +00:00
|
|
|
}
|
2019-02-17 19:49:37 +00:00
|
|
|
fs.unlink(video.dir+filename,function(err){
|
|
|
|
fs.stat(video.dir+filename,function(err){
|
|
|
|
if(!err){
|
|
|
|
s.file('delete',video.dir+filename)
|
|
|
|
}
|
|
|
|
})
|
2018-11-05 20:55:58 +00:00
|
|
|
})
|
|
|
|
})
|
2019-02-17 19:49:37 +00:00
|
|
|
videoQuery.push('(`mid`=? AND `ke`=? AND `time`=?)')
|
|
|
|
queryValues = queryValues.concat([video.id,video.ke,time])
|
2018-11-05 20:55:58 +00:00
|
|
|
})
|
2019-02-17 19:49:37 +00:00
|
|
|
query += videoQuery.join(' OR ')
|
|
|
|
s.sqlQuery(query,queryValues,function(err){
|
|
|
|
if(err){
|
|
|
|
s.systemLog(lang['List of Videos Delete Error'],err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
videos.chunk(100).forEach(function(videosChunk){
|
|
|
|
deleteSetOfVideos(videosChunk)
|
2018-11-05 20:55:58 +00:00
|
|
|
})
|
|
|
|
}
|
2018-09-30 22:44:04 +00:00
|
|
|
s.deleteVideoFromCloudExtensions = {}
|
|
|
|
s.deleteVideoFromCloudExtensionsRunner = function(e,storageType,video){
|
|
|
|
// e = user
|
|
|
|
if(!storageType){
|
|
|
|
var videoDetails = JSON.parse(r.details)
|
|
|
|
videoDetails.type = videoDetails.type || 's3'
|
|
|
|
}
|
|
|
|
if(s.deleteVideoFromCloudExtensions[storageType]){
|
|
|
|
s.deleteVideoFromCloudExtensions[storageType](e,video,function(){
|
|
|
|
s.tx({
|
|
|
|
f: 'video_delete_cloud',
|
|
|
|
mid: e.mid,
|
|
|
|
ke: e.ke,
|
|
|
|
time: e.time,
|
|
|
|
end: e.end
|
|
|
|
},'GRP_'+e.ke);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-09-29 15:00:51 +00:00
|
|
|
s.deleteVideoFromCloud = function(e){
|
2018-10-12 00:36:27 +00:00
|
|
|
// e = video object
|
2018-10-08 23:54:53 +00:00
|
|
|
s.checkDetails(e)
|
2018-09-30 22:44:04 +00:00
|
|
|
var videoSelector = [e.id,e.ke,new Date(e.time)]
|
|
|
|
s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE `mid`=? AND `ke`=? AND `time`=?',videoSelector,function(err,r){
|
|
|
|
if(r&&r[0]){
|
|
|
|
r = r[0]
|
|
|
|
s.sqlQuery('DELETE FROM `Cloud Videos` WHERE `mid`=? AND `ke`=? AND `time`=?',videoSelector,function(){
|
|
|
|
s.deleteVideoFromCloudExtensionsRunner(e,r)
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
// console.log('Delete Failed',e)
|
|
|
|
// console.error(err)
|
|
|
|
}
|
2018-09-29 15:00:51 +00:00
|
|
|
})
|
2018-09-28 05:37:08 +00:00
|
|
|
}
|
2018-10-10 06:56:13 +00:00
|
|
|
s.orphanedVideoCheck = function(monitor,checkMax,callback,forceCheck){
|
|
|
|
var finish = function(orphanedFilesCount){
|
|
|
|
if(callback)callback(orphanedFilesCount)
|
|
|
|
}
|
|
|
|
if(forceCheck === true || config.insertOrphans === true){
|
|
|
|
if(!checkMax){
|
2019-06-07 23:55:25 +00:00
|
|
|
checkMax = config.orphanedVideoCheckMax || 2
|
2018-10-10 06:56:13 +00:00
|
|
|
}
|
2019-05-15 17:46:24 +00:00
|
|
|
|
2018-10-10 06:56:13 +00:00
|
|
|
var videosDirectory = s.getVideoDirectory(monitor)// + s.formattedTime(video.time) + '.' + video.ext
|
|
|
|
fs.readdir(videosDirectory,function(err,files){
|
|
|
|
if(files && files.length > 0){
|
2018-10-18 04:42:32 +00:00
|
|
|
var fiveRecentFiles = files.slice(files.length - checkMax,files.length)
|
2018-10-10 06:56:13 +00:00
|
|
|
var completedFile = 0
|
|
|
|
var orphanedFilesCount = 0
|
|
|
|
var fileComplete = function(){
|
|
|
|
++completedFile
|
|
|
|
if(fiveRecentFiles.length === completedFile){
|
|
|
|
finish(orphanedFilesCount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fiveRecentFiles.forEach(function(filename){
|
|
|
|
if(/T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]./.test(filename)){
|
|
|
|
var queryValues = [
|
|
|
|
monitor.ke,
|
|
|
|
monitor.mid,
|
|
|
|
s.nameToTime(filename)
|
|
|
|
]
|
|
|
|
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND mid=? AND time=? LIMIT 1',queryValues,function(err,rows){
|
|
|
|
if(!err && (!rows || !rows[0])){
|
|
|
|
++orphanedFilesCount
|
|
|
|
var video = rows[0]
|
|
|
|
s.insertCompletedVideo(monitor,{
|
|
|
|
file : filename
|
|
|
|
},function(){
|
|
|
|
fileComplete()
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
fileComplete()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
}
|
2019-01-29 01:41:14 +00:00
|
|
|
s.streamMp4FileOverHttp = function(filePath,req,res){
|
|
|
|
var ext = filePath.split('.')
|
2019-09-07 00:02:17 +00:00
|
|
|
ext = ext[ext.length - 1]
|
2019-01-29 01:41:14 +00:00
|
|
|
var total = fs.statSync(filePath).size;
|
|
|
|
if (req.headers['range']) {
|
|
|
|
try{
|
|
|
|
var range = req.headers.range;
|
|
|
|
var parts = range.replace(/bytes=/, "").split("-");
|
|
|
|
var partialstart = parts[0];
|
|
|
|
var partialend = parts[1];
|
|
|
|
var start = parseInt(partialstart, 10);
|
|
|
|
var end = partialend ? parseInt(partialend, 10) : total-1;
|
|
|
|
var chunksize = (end-start)+1;
|
|
|
|
var file = fs.createReadStream(filePath, {start: start, end: end});
|
2019-09-07 00:02:17 +00:00
|
|
|
req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+ext }
|
2019-01-29 01:41:14 +00:00
|
|
|
req.writeCode=206
|
|
|
|
}catch(err){
|
2019-09-07 00:02:17 +00:00
|
|
|
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
|
2019-01-29 01:41:14 +00:00
|
|
|
var file = fs.createReadStream(filePath)
|
|
|
|
req.writeCode=200
|
|
|
|
}
|
|
|
|
} else {
|
2019-09-07 00:02:17 +00:00
|
|
|
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+ext};
|
2019-01-29 01:41:14 +00:00
|
|
|
var file = fs.createReadStream(filePath)
|
|
|
|
req.writeCode=200
|
|
|
|
}
|
|
|
|
if(req.query.downloadName){
|
|
|
|
req.headerWrite['content-disposition']='attachment; filename="'+req.query.downloadName+'"';
|
|
|
|
}
|
|
|
|
res.writeHead(req.writeCode,req.headerWrite);
|
|
|
|
file.on('close',function(){
|
|
|
|
res.end()
|
|
|
|
})
|
|
|
|
file.pipe(res)
|
|
|
|
return file
|
|
|
|
}
|
2019-04-19 00:54:07 +00:00
|
|
|
s.createVideoFromTimelapse = function(timelapseFrames,framesPerSecond,callback){
|
|
|
|
framesPerSecond = parseInt(framesPerSecond)
|
|
|
|
if(!framesPerSecond || isNaN(framesPerSecond))framesPerSecond = 2
|
2019-04-03 03:47:03 +00:00
|
|
|
var frames = timelapseFrames.reverse()
|
|
|
|
var ke = frames[0].ke
|
|
|
|
var mid = frames[0].mid
|
2019-04-19 00:54:07 +00:00
|
|
|
var finalFileName = frames[0].filename.split('.')[0] + '_' + frames[frames.length - 1].filename.split('.')[0] + `-${framesPerSecond}fps`
|
2019-04-03 03:47:03 +00:00
|
|
|
var concatFiles = []
|
|
|
|
var createLocation
|
|
|
|
frames.forEach(function(frame,frameNumber){
|
|
|
|
var selectedDate = frame.filename.split('T')[0]
|
|
|
|
var fileLocationMid = `${frame.ke}/${frame.mid}_timelapse/${selectedDate}/`
|
|
|
|
frame.details = s.parseJSON(frame.details)
|
|
|
|
var fileLocation
|
|
|
|
if(frame.details.dir){
|
|
|
|
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
|
|
|
|
}else{
|
|
|
|
fileLocation = `${s.dir.videos}`
|
|
|
|
}
|
|
|
|
fileLocation = `${fileLocation}${fileLocationMid}${frame.filename}`
|
|
|
|
concatFiles.push(fileLocation)
|
|
|
|
if(frameNumber === 0){
|
|
|
|
createLocation = fileLocationMid
|
|
|
|
}
|
|
|
|
})
|
2019-09-08 06:19:21 +00:00
|
|
|
if(concatFiles.length > 30){
|
|
|
|
var commandTempLocation = `${s.dir.streams}${ke}/${mid}/mergeJpegs_${finalFileName}.sh`
|
|
|
|
var finalMp4OutputLocation = `${s.dir.fileBin}${ke}/${mid}/${finalFileName}.mp4`
|
|
|
|
if(!s.group[ke].activeMonitors[mid].buildingTimelapseVideo){
|
|
|
|
if(!fs.existsSync(finalMp4OutputLocation)){
|
|
|
|
var currentFile = 0
|
|
|
|
var completionTimeout
|
|
|
|
var commandString = `ffmpeg -y -pattern_type glob -f image2pipe -vcodec mjpeg -r ${framesPerSecond} -analyzeduration 10 -i - -q:v 1 -c:v libx264 -r ${framesPerSecond} "${finalMp4OutputLocation}"`
|
|
|
|
fs.writeFileSync(commandTempLocation,commandString)
|
|
|
|
var videoBuildProcess = spawn('sh',[commandTempLocation])
|
|
|
|
videoBuildProcess.stderr.on('data',function(data){
|
|
|
|
// console.log(data.toString())
|
|
|
|
clearTimeout(completionTimeout)
|
|
|
|
completionTimeout = setTimeout(function(){
|
|
|
|
if(currentFile === concatFiles.length - 1){
|
|
|
|
videoBuildProcess.kill('SIGTERM')
|
|
|
|
}
|
|
|
|
},4000)
|
|
|
|
})
|
|
|
|
videoBuildProcess.on('exit',function(data){
|
|
|
|
var timeNow = new Date()
|
|
|
|
var fileStats = fs.statSync(finalMp4OutputLocation)
|
|
|
|
var details = {}
|
|
|
|
s.sqlQuery('INSERT INTO `Files` (ke,mid,details,name,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),finalFileName + '.mp4',fileStats.size,timeNow])
|
2020-02-01 02:30:24 +00:00
|
|
|
s.setDiskUsedForGroup({ke: ke},fileStats.size / 1048576,'fileBin')
|
2019-09-08 06:19:21 +00:00
|
|
|
fs.unlink(commandTempLocation,function(){
|
2019-04-03 03:47:03 +00:00
|
|
|
|
2019-09-08 06:19:21 +00:00
|
|
|
})
|
|
|
|
delete(s.group[ke].activeMonitors[mid].buildingTimelapseVideo)
|
2019-04-03 03:47:03 +00:00
|
|
|
})
|
2019-09-08 06:19:21 +00:00
|
|
|
var readFile = function(){
|
|
|
|
var filePath = concatFiles[currentFile]
|
|
|
|
// console.log(filePath,currentFile,'/',concatFiles.length - 1)
|
|
|
|
fs.readFile(filePath,function(err,buffer){
|
|
|
|
if(!err)videoBuildProcess.stdin.write(buffer)
|
|
|
|
if(currentFile === concatFiles.length - 1){
|
|
|
|
//is last
|
2019-04-03 03:47:03 +00:00
|
|
|
|
2019-09-08 06:19:21 +00:00
|
|
|
}else{
|
|
|
|
setTimeout(function(){
|
|
|
|
++currentFile
|
|
|
|
readFile()
|
|
|
|
},1/framesPerSecond)
|
|
|
|
}
|
|
|
|
})
|
2019-04-03 03:47:03 +00:00
|
|
|
}
|
2019-09-08 06:19:21 +00:00
|
|
|
readFile()
|
|
|
|
s.group[ke].activeMonitors[mid].buildingTimelapseVideo = videoBuildProcess
|
|
|
|
callback({
|
|
|
|
ok: true,
|
|
|
|
fileExists: false,
|
|
|
|
fileLocation: finalMp4OutputLocation,
|
|
|
|
msg: lang['Started Building']
|
|
|
|
})
|
|
|
|
}else{
|
|
|
|
callback({
|
|
|
|
ok: false,
|
|
|
|
fileExists: true,
|
|
|
|
fileLocation: finalMp4OutputLocation,
|
|
|
|
msg: lang['Already exists']
|
|
|
|
})
|
2019-04-03 03:47:03 +00:00
|
|
|
}
|
|
|
|
}else{
|
2019-04-06 05:27:22 +00:00
|
|
|
callback({
|
|
|
|
ok: false,
|
2019-09-08 06:19:21 +00:00
|
|
|
fileExists: false,
|
2019-04-06 05:27:22 +00:00
|
|
|
fileLocation: finalMp4OutputLocation,
|
2019-09-08 06:19:21 +00:00
|
|
|
msg: lang.Building
|
2019-04-06 05:27:22 +00:00
|
|
|
})
|
2019-04-03 03:47:03 +00:00
|
|
|
}
|
|
|
|
}else{
|
2019-04-06 05:27:22 +00:00
|
|
|
callback({
|
|
|
|
ok: false,
|
|
|
|
fileExists: false,
|
2019-09-08 06:19:21 +00:00
|
|
|
msg: lang.notEnoughFramesText1
|
2019-04-06 05:27:22 +00:00
|
|
|
})
|
2019-04-03 03:47:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-19 00:54:07 +00:00
|
|
|
s.getVideoStorageIndex = function(video){
|
2019-08-14 17:42:41 +00:00
|
|
|
var details = s.parseJSON(video.details) || {}
|
2019-06-05 23:06:59 +00:00
|
|
|
var storageId = details.storageId
|
2019-07-08 03:39:41 +00:00
|
|
|
if(s.group[video.ke] && s.group[video.ke].activeMonitors[video.id] && s.group[video.ke].activeMonitors[video.id].addStorageId)storageId = s.group[video.ke].activeMonitors[video.id].addStorageId
|
2019-04-19 00:54:07 +00:00
|
|
|
if(storageId){
|
|
|
|
return s.group[video.ke].addStorageUse[storageId]
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
2018-09-28 05:37:08 +00:00
|
|
|
}
|