Merge branch 'dev' into 'master'

Swirl of Shamshir : Critical Fixes

See merge request Shinobi-Systems/Shinobi!437
fix-multi-trigger
Moe 2023-01-26 21:48:16 +00:00
commit f2c6578b82
21 changed files with 1664 additions and 1573 deletions

View File

@ -60,6 +60,13 @@ module.exports = {
},timeoutInSeconds * 1000 || 1000)
},queueItemsRunningInParallel || 3)
},
createQueueAwaited: (timeoutInSeconds, queueItemsRunningInParallel) => {
return async.queue(function(action, callback) {
setTimeout(function(){
action().then(callback)
},timeoutInSeconds * 1000 || 1000)
},queueItemsRunningInParallel || 3)
},
copyObject: (obj) => {
return Object.assign({},obj)
},

View File

@ -1,7 +1,8 @@
module.exports = function(s){
const configLocation = process.argv[2]
const superLocation = process.argv[3]
s.location = {
super : s.mainDirectory+'/super.json',
super : superLocation && superLocation.endsWith('.json') ? superLocation : s.mainDirectory+'/super.json',
config : configLocation && configLocation.endsWith('.json') ? configLocation : s.mainDirectory+'/conf.json',
languages : s.mainDirectory+'/languages'
}

View File

@ -413,6 +413,41 @@ module.exports = function(s,config,lang){
setHomePositionTimeout(event)
}
}
function setHomePositionPreset(e){
return new Promise((resolve) => {
setTimeout(() => {
setPresetForCurrentPosition({
ke: e.ke,
id: monitorId
},(endData) => {
if(endData.ok === false){
setTimeout(() => {
setPresetForCurrentPosition({
ke: e.ke,
id: monitorId
},(endData) => {
if(endData.ok === false){
setTimeout(() => {
setPresetForCurrentPosition({
ke: e.ke,
id: monitorId
},(endData) => {
console.log(endData)
resolve()
})
},5000)
}else{
resolve()
}
})
},5000)
}else{
resolve()
}
})
},5000)
})
}
return {
startMove,
stopMove,
@ -421,5 +456,6 @@ module.exports = function(s,config,lang){
setPresetForCurrentPosition,
moveToPresetPosition,
moveCameraPtzToMatrix,
setHomePositionPreset,
}
}

View File

@ -230,7 +230,7 @@ module.exports = function(s,config,lang,app,io){
var firstDroppedPart = pathPieces[2]
var monitorEventDropDir = s.dir.dropInEvents + ke + '/' + mid + '/'
var deleteKey = monitorEventDropDir + firstDroppedPart
onFileOrFolderFound(monitorEventDropDir + firstDroppedPart,deleteKey,Object.assign(s.group[ke].rawMonitorConfigurations[mid],{}))
onFileOrFolderFound(monitorEventDropDir + firstDroppedPart,deleteKey,Object.assign({},s.group[ke].rawMonitorConfigurations[mid]))
})
resolve({root: s.dir.dropInEvents + user.ke})
}else{

View File

@ -44,64 +44,86 @@ module.exports = async (s,config,lang,onFinish) => {
buildTimelapseOutput(e),
];
if(allOutputs.filter(output => !!output).length > 0){
([
buildMainInput(e),
]).concat(allOutputs).forEach(function(commandStringPart){
ffmpegCommand.push(commandStringPart)
})
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
extender(e,ffmpegCommand)
})
const stdioPipes = createPipeArray(e)
const ffmpegCommandString = ffmpegCommand.join(' ')
//hold ffmpeg command for log stream
activeMonitor.ffmpeg = sanitizedFfmpegCommand(e,ffmpegCommandString)
//clean the string of spatial impurities and split for spawn()
const ffmpegCommandParsed = splitForFFPMEG(ffmpegCommandString)
try{
fs.rmSync(e.sdir + 'cmd.txt')
}catch(err){
}
fs.writeFileSync(e.sdir + 'cmd.txt',JSON.stringify({
dataPortToken: dataPortToken,
cmd: ffmpegCommandParsed,
pipes: stdioPipes.length,
rawMonitorConfig: s.group[e.ke].rawMonitorConfigurations[e.id],
globalInfo: {
config: config,
isAtleatOneDetectorPluginConnected: s.isAtleatOneDetectorPluginConnected
}
},null,3),'utf8')
var cameraCommandParams = [
config.monitorDaemonPath ? config.monitorDaemonPath : __dirname + '/cameraThread/singleCamera.js',
config.ffmpegDir,
e.sdir + 'cmd.txt'
]
const cameraProcess = spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes})
if(config.debugLog === true && config.debugLogMonitors === true){
cameraProcess.stderr.on('data',(data) => {
const string = data.toString()
var checkLog = function(x){return string.indexOf(x)>-1}
switch(true){
case checkLog('pkt->duration = 0'):
case checkLog('[hls @'):
case checkLog('Past duration'):
case checkLog('Last message repeated'):
case checkLog('Non-monotonous DTS'):
case checkLog('NULL @'):
case checkLog('RTP: missed'):
case checkLog('deprecated pixel format used'):
if(!config.debugLogMonitorsVerbose){
return;
}
break;
return new Promise((resolve) => {
var hasResolved = false
var completionTimer = null;
function completeResolve(data){
clearTimeout(completionTimer)
if(!hasResolved){
hasResolved = true
resolve(data)
}
console.log(`${e.ke} ${e.name} (${e.mid})`)
console.log(data.toString())
})
}
return cameraProcess
}
try{
([
buildMainInput(e),
]).concat(allOutputs).forEach(function(commandStringPart){
ffmpegCommand.push(commandStringPart)
})
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
extender(e,ffmpegCommand)
})
const stdioPipes = createPipeArray(e)
const ffmpegCommandString = ffmpegCommand.join(' ')
//hold ffmpeg command for log stream
activeMonitor.ffmpeg = sanitizedFfmpegCommand(e,ffmpegCommandString)
//clean the string of spatial impurities and split for spawn()
const ffmpegCommandParsed = splitForFFPMEG(ffmpegCommandString)
try{
fs.rmSync(e.sdir + 'cmd.txt')
}catch(err){
}
fs.writeFileSync(e.sdir + 'cmd.txt',JSON.stringify({
dataPortToken: dataPortToken,
cmd: ffmpegCommandParsed,
pipes: stdioPipes.length,
rawMonitorConfig: s.group[e.ke].rawMonitorConfigurations[e.id],
globalInfo: {
config: config,
isAtleatOneDetectorPluginConnected: s.isAtleatOneDetectorPluginConnected
}
},null,3),'utf8')
var cameraCommandParams = [
config.monitorDaemonPath ? config.monitorDaemonPath : __dirname + '/cameraThread/singleCamera.js',
config.ffmpegDir,
e.sdir + 'cmd.txt'
]
const cameraProcess = spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes})
if(config.debugLog === true && config.debugLogMonitors === true){
cameraProcess.stderr.on('data',(data) => {
const string = data.toString()
var checkLog = function(x){return string.indexOf(x)>-1}
switch(true){
case checkLog('pkt->duration = 0'):
case checkLog('[hls @'):
case checkLog('Past duration'):
case checkLog('Last message repeated'):
case checkLog('Non-monotonous DTS'):
case checkLog('NULL @'):
case checkLog('RTP: missed'):
case checkLog('deprecated pixel format used'):
if(!config.debugLogMonitorsVerbose){
return;
}
break;
}
console.log(`${e.ke} ${e.name} (${e.mid})`)
console.log(data.toString())
})
}
cameraProcess.stdio[5].once('data',(data) => {
completeResolve(cameraProcess)
})
completionTimer = setTimeout(() => {
completeResolve(cameraProcess)
},20000)
}catch(err){
completeResolve(null)
s.systemLog(err)
return null
}
})
}else{
return null
}

View File

@ -760,9 +760,10 @@ module.exports = (s,config,lang) => {
const videoFlags = []
const inputMap = buildInputMap(e,e.details.input_map_choices.record_timelapse)
const { videoWidth, videoHeight } = validateDimensions(e.details.record_timelapse_scale_x,e.details.record_timelapse_scale_y)
const creationFps = (1 / (!isNaN(parseFloat(e.details.record_timelapse_fps)) ? parseFloat(e.details.record_timelapse_fps) : 900)).toFixed(3);
if(videoWidth && videoHeight)videoFlags.push(`-s ${videoWidth}x${videoHeight}`)
if(inputMap)videoFlags.push(inputMap)
videoFilters.push(`fps=${(1 / (!isNaN(parseFloat(e.details.record_timelapse_fps)) ? parseFloat(e.details.record_timelapse_fps) : 900)).toFixed(3)}`)
videoFilters.push(`fps=${creationFps}`)
if(e.details.record_timelapse_vf)videoFilters.push(e.details.record_timelapse_vf)
if(e.details.record_timelapse_watermark === "1" && e.details.record_timelapse_watermark_location){
videoFilters.push(buildWatermarkFiltersFromConfiguration('record_timelapse_',e))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -80,10 +80,12 @@ module.exports = function(s,config,lang,getSnapshot){
} : undefined)
.then(res => res.text())
.then((text) => {
console.error(`Webhook Response`,text)
response.response = text;
resolve(response)
})
.catch((err) => {
console.error(`Webhook Fail`)
response.ok = false;
response.error = err;
s.userLog({
@ -104,23 +106,15 @@ module.exports = function(s,config,lang,getSnapshot){
const onEventTriggerForGlobalWebhook = async (d,filter) => {
let filesSent = 0;
const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id]
// d = event object
if((filter.global_webhook || monitorConfig.details.notify_global_webhook === '1') && !s.group[d.ke].activeMonitors[d.id].detector_global_webhook){
var detector_global_webhook_timeout
if(!monitorConfig.details.detector_global_webhook_timeout||monitorConfig.details.detector_global_webhook_timeout===''){
detector_global_webhook_timeout = 1000 * 60 * 10;
}else{
detector_global_webhook_timeout = parseFloat(monitorConfig.details.detector_global_webhook_timeout) * 1000 * 60;
}
s.group[d.ke].activeMonitors[d.id].detector_global_webhook = setTimeout(function(){
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_global_webhook);
s.group[d.ke].activeMonitors[d.id].detector_global_webhook = null
},detector_global_webhook_timeout)
if((filter.global_webhook || monitorConfig.details.notify_global_webhook === '1')){
await getSnapshot(d,monitorConfig)
if(d.screenshotBuffer){
sendMessage({
title: lang.Event+' - '+d.screenshotName,
description: lang.EventText1+' '+d.currentTimestamp,
ke: d.ke,
mid: d.id,
eventDetails: d.details
},[
{
type: 'photo',
@ -134,6 +128,8 @@ module.exports = function(s,config,lang,getSnapshot){
sendMessage({
title: lang.Event,
description: lang.EventText1+' '+d.currentTimestamp,
ke: d.ke,
mid: d.id,
eventDetails: d.details
},[],d.ke)
}
@ -197,13 +193,12 @@ module.exports = function(s,config,lang,getSnapshot){
"value": "1"
}
]
},
)
}
);
s.definitions["Account Settings"].blocks["2-Factor Authentication"].info.push({
"name": "detail=factor_global_webhook",
"field": lang.Webhook,
"default": "1",
"example": "",
"fieldType": "select",
"possible": [
{
@ -215,7 +210,7 @@ module.exports = function(s,config,lang,getSnapshot){
"value": "1"
}
]
})
});
s.definitions["Account Settings"].blocks["Webhook"] = {
"evaluation": "$user.details.use_global_webhook !== '0'",
"name": lang.Webhook,
@ -245,45 +240,27 @@ module.exports = function(s,config,lang,getSnapshot){
"placeholder": "http://your-webhook-point/onEvent/{{INNER_EVENT_TITLE}}?info={{INNER_EVENT_INFO}}",
"field": lang["Webhook URL"],
"form-group-class":"u_global_webhook_input u_global_webhook_1",
},
{
hidden: true,
"name": "detail=factor_global_webhook",
"field": lang["2-Factor Authentication"],
"form-group-class":"u_global_webhook_input u_global_webhook_1",
"default": "1",
"example": "",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
}
]
}
s.definitions["Event Filters"].blocks["Action for Selected"].info.push({
"name": "actions=global_webhook",
"field": lang['Webhook'],
"fieldType": "select",
"form-group-class": "actions-row",
"default": "",
"example": "1",
"possible": [
{
"name": lang['Original Choice'],
"value": "",
"selected": true
},
{
"name": lang.Yes,
"value": "1",
}
]
})
"name": "actions=global_webhook",
"field": lang['Webhook'],
"fieldType": "select",
"form-group-class": "actions-row",
"default": "",
"example": "1",
"possible": [
{
"name": lang['Original Choice'],
"value": "",
"selected": true
},
{
"name": lang.Yes,
"value": "1",
}
]
},
)
}

View File

@ -76,7 +76,7 @@ module.exports = function(s,config,lang,io){
}
}
if(checkedAdminUsers[monitor.ke]){
setTimeout(function(){
setTimeout(async function(){
if(!orphanedVideosForMonitors[monitor.ke])orphanedVideosForMonitors[monitor.ke] = {}
if(!orphanedVideosForMonitors[monitor.ke][monitor.mid])orphanedVideosForMonitors[monitor.ke][monitor.mid] = 0
s.initiateMonitorObject(monitor)
@ -88,7 +88,8 @@ module.exports = function(s,config,lang,io){
code: 5
});
const monObj = Object.assign({},monitor,{id : monitor.mid})
s.camera(monitor.mode,monObj)
await s.camera('stop',monObj);
await s.camera(monitor.mode,monObj);
checkAnother()
},1000)
}else{
@ -171,7 +172,7 @@ module.exports = function(s,config,lang,io){
var addStorageData = {
files: [],
videos: [],
timelapeFrames: [],
timelapseFrames: [],
}
if(videos && videos[0]){
videos.forEach(function(video){
@ -189,7 +190,7 @@ module.exports = function(s,config,lang,io){
if(!frame.details.dir){
usedSpaceTimelapseFrames += frame.size
}else{
addStorageData.timelapeFrames.push(frame)
addStorageData.timelapseFrames.push(frame)
}
})
}
@ -314,6 +315,8 @@ module.exports = function(s,config,lang,io){
storageIndex.path = path
storageIndex.usedSpace = 0
storageIndex.sizeLimit = parseFloat(storageData.limit) || parseFloat(userDetails.size) || 10000
storageIndex.videoPercent = parseFloat(storageData.videoPercent) || parseFloat(userDetails.size_video_percent) || 95
storageIndex.timelapsePercent = parseFloat(storageData.timelapsePercent) || parseFloat(userDetails.size_timelapse_percent) || 5
var usedSpaceVideos = 0
var usedSpaceTimelapseFrames = 0
var usedSpaceFilebin = 0
@ -326,7 +329,7 @@ module.exports = function(s,config,lang,io){
}
if(timelapseFrames && timelapseFrames[0]){
timelapseFrames.forEach(function(frame){
if(video.details.dir === storage.value){
if(frame.details.dir === storage.value){
usedSpaceTimelapseFrames += frame.size
}
})

View File

@ -65,12 +65,25 @@ module.exports = function(s,config,lang,app,io){
}
}
s.insertTimelapseFrameDatabaseRow = function(e,queryInfo,filePath){
const groupKey = e.ke
const theGroup = s.group[groupKey]
const frameDetails = Object.assign({},s.parseJSON(queryInfo.details) || {})
const storageId = e.details.dir
const storageIndex = theGroup.addStorageUse[storageId]
const fileSize = queryInfo.size / 1048576
s.knexQuery({
action: "insert",
table: "Timelapse Frames",
insert: queryInfo
})
s.setDiskUsedForGroup(e.ke,queryInfo.size / 1048576,'timelapeFrames')
if(storageIndex){
s.setDiskUsedForGroupAddStorage(groupKey,{
size: fileSize,
storageIndex: storageIndex
},'timelapseFrames')
}else{
s.setDiskUsedForGroup(groupKey, fileSize, 'timelapseFrames')
}
s.purgeDiskForGroup(e.ke)
s.onInsertTimelapseFrameExtensions.forEach(function(extender){
extender(e,queryInfo,filePath)
@ -149,7 +162,7 @@ module.exports = function(s,config,lang,app,io){
where: frameSelector,
limit: 1
},async function(){
s.setDiskUsedForGroup(e.ke,-(r.size / 1048576),'timelapeFrames')
s.setDiskUsedForGroup(e.ke,-(r.size / 1048576),'timelapseFrames')
s.file('delete',e.fileLocation)
const fileDirectory = getFileDirectory(folderPath);
const folderIsEmpty = (await fs.promises.readdir(folderPath)).filter(file => file.indexOf('.jpg') > -1).length === 0;

View File

@ -3,6 +3,7 @@ var events = require('events');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var async = require("async");
const { createQueueAwaited } = require('./common.js')
module.exports = function(s,config,lang){
const {
deleteSetOfVideos,
@ -11,6 +12,7 @@ module.exports = function(s,config,lang){
deleteAddStorageVideos,
deleteMainVideos,
deleteTimelapseFrames,
deleteAddStorageTimelapseFrames,
deleteFileBinFiles,
deleteCloudVideos,
deleteCloudTimelapseFrames,
@ -29,13 +31,16 @@ module.exports = function(s,config,lang){
deleteMainVideos(groupKey,() => {
s.debugLog(`${groupKey} deleteTimelapseFrames`)
deleteTimelapseFrames(groupKey,() => {
s.debugLog(`${groupKey} deleteFileBinFiles`)
deleteFileBinFiles(groupKey,() => {
s.debugLog(`${groupKey} deleteAddStorageVideos`)
deleteAddStorageVideos(groupKey,() => {
s.group[groupKey].sizePurging = false
s.sendDiskUsedAmountToClients(groupKey)
callback();
s.debugLog(`${groupKey} deleteAddStorageTimelapseFrames`)
deleteAddStorageTimelapseFrames(groupKey,() => {
s.debugLog(`${groupKey} deleteFileBinFiles`)
deleteFileBinFiles(groupKey,() => {
s.debugLog(`${groupKey} deleteAddStorageVideos`)
deleteAddStorageVideos(groupKey,() => {
s.group[groupKey].sizePurging = false
s.sendDiskUsedAmountToClients(groupKey)
callback();
})
})
})
})
@ -128,22 +133,25 @@ module.exports = function(s,config,lang){
if(!s.group[e.ke].init){
s.group[e.ke].init={}
}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse={}};
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
if(!s.group[e.ke].users){s.group[e.ke].users={}}
if(!s.group[e.ke].dashcamUsers){s.group[e.ke].dashcamUsers={}}
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse = {}}
const theGroup = s.group[e.ke]
if(!theGroup.addStorageUse){theGroup.addStorageUse={}};
if(!theGroup.fileBin){theGroup.fileBin={}};
if(!theGroup.users){theGroup.users={}}
if(!theGroup.dashcamUsers){theGroup.dashcamUsers={}}
if(!theGroup.sizePurgeQueue){theGroup.sizePurgeQueue=[]}
if(!theGroup.addStorageUse){theGroup.addStorageUse = {}}
if(!e.limit||e.limit===''){e.limit=10000}else{e.limit=parseFloat(e.limit)}
//save global space limit for group key (mb)
s.group[e.ke].sizeLimit = e.limit || s.group[e.ke].sizeLimit || 10000
s.group[e.ke].sizeLimitVideoPercent = parseFloat(s.group[e.ke].init.size_video_percent) || 90
s.group[e.ke].sizeLimitTimelapseFramesPercent = parseFloat(s.group[e.ke].init.size_timelapse_percent) || 5
s.group[e.ke].sizeLimitFileBinPercent = parseFloat(s.group[e.ke].init.size_filebin_percent) || 5
theGroup.sizeLimit = e.limit || theGroup.sizeLimit || 10000
theGroup.sizeLimitVideoPercent = parseFloat(theGroup.init.size_video_percent) || 90
theGroup.sizeLimitTimelapseFramesPercent = parseFloat(theGroup.init.size_timelapse_percent) || 5
theGroup.sizeLimitFileBinPercent = parseFloat(theGroup.init.size_filebin_percent) || 5
//save global used space as megabyte value
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace || ((e.size || 0) / 1048576)
theGroup.usedSpace = theGroup.usedSpace || ((e.size || 0) / 1048576)
//emit the changes to connected users
s.sendDiskUsedAmountToClients(e.ke)
// create monitor management queue
theGroup.startMonitorInQueue = createQueueAwaited(0.5, 1)
}
s.loadGroupApps = function(e){
// e = user
@ -186,7 +194,7 @@ module.exports = function(s,config,lang){
//change global size value
cloudDisk.usedSpace = cloudDisk.usedSpace + amount
switch(storagePoint){
case'timelapeFrames':
case'timelapseFrames':
cloudDisk.usedSpaceTimelapseFrames += amount
break;
case'fileBin':
@ -221,7 +229,7 @@ module.exports = function(s,config,lang){
s.group[e.ke].usedSpace += currentChange
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace < 0 ? 0 : s.group[e.ke].usedSpace
switch(storageType){
case'timelapeFrames':
case'timelapseFrames':
s.group[e.ke].usedSpaceTimelapseFrames += currentChange
s.group[e.ke].usedSpaceTimelapseFrames = s.group[e.ke].usedSpaceTimelapseFrames < 0 ? 0 : s.group[e.ke].usedSpaceTimelapseFrames
break;
@ -252,7 +260,7 @@ module.exports = function(s,config,lang){
//change global size value
storageIndex.usedSpace += currentSize
switch(storageType){
case'timelapeFrames':
case'timelapseFrames':
storageIndex.usedSpaceTimelapseFrames += currentSize
break;
case'fileBin':
@ -338,6 +346,7 @@ module.exports = function(s,config,lang){
if(details.size){formDetails.size = details.size;}
if(details.days){formDetails.days = details.days;}
}
const theGroup = s.group[d.ke]
var newSize = parseFloat(formDetails.size) || 10000
//load addStorageUse
var currentStorageNumber = 0
@ -361,11 +370,10 @@ module.exports = function(s,config,lang){
storageIndex.name = storage.name
storageIndex.path = path
storageIndex.usedSpace = storageIndex.usedSpace || 0
if(detailsContainerAddStorage && detailsContainerAddStorage[path] && detailsContainerAddStorage[path].limit){
storageIndex.sizeLimit = parseFloat(detailsContainerAddStorage[path].limit)
}else{
storageIndex.sizeLimit = newSize
}
const storageInfoToSave = detailsContainerAddStorage && detailsContainerAddStorage[path] ? detailsContainerAddStorage[path] : {}
storageIndex.sizeLimit = parseFloat(storageInfoToSave.limit) || newSize
storageIndex.videoPercent = parseFloat(storageInfoToSave.videoPercent) || theGroup.sizeLimitVideoPercent
storageIndex.timelapsePercent = parseFloat(storageInfoToSave.timelapsePercent) || theGroup.sizeLimitTimelapseFramesPercent
}
readStorageArray()
///

View File

@ -92,13 +92,6 @@ module.exports = (s,config,lang) => {
whereGroup.push(queryGroup)
fs.rm(fileLocationMid,function(err){
++completedCheck
if(err){
fs.stat(fileLocationMid,function(err){
if(!err){
fs.unlink(fileLocationMid)
}
})
}
const whereGroupLength = whereGroup.length
if(whereGroupLength > 0 && whereGroupLength === completedCheck){
whereQuery[1] = whereGroup
@ -115,9 +108,9 @@ module.exports = (s,config,lang) => {
s.setDiskUsedForGroupAddStorage(groupKey,{
size: -(frame.size/1048576),
storageIndex: storageIndex
},'timelapeFrames')
},'timelapseFrames')
}else{
s.setDiskUsedForGroup(groupKey,-(frame.size/1048576),'timelapeFrames')
s.setDiskUsedForGroup(groupKey,-(frame.size/1048576),'timelapseFrames')
}
// s.tx({
// f: 'timelapse_frame_delete',
@ -193,7 +186,8 @@ module.exports = (s,config,lang) => {
return deleteAddStorageVideos(groupKey,callback)
}
var currentStorageNumber = 0
var readStorageArray = function(){
function readStorageArray(){
const theGroup = s.group[groupKey]
setTimeout(function(){
reRunCheck = readStorageArray
var storage = s.listOfStorage[currentStorageNumber]
@ -203,14 +197,15 @@ module.exports = (s,config,lang) => {
return
}
var storageId = storage.value
if(storageId === '' || !s.group[groupKey].addStorageUse[storageId]){
if(storageId === '' || !theGroup.addStorageUse[storageId]){
++currentStorageNumber
readStorageArray()
return
}
var storageIndex = s.group[groupKey].addStorageUse[storageId]
var storageIndex = theGroup.addStorageUse[storageId]
//run purge command
if(storageIndex.usedSpace > (storageIndex.sizeLimit * (storageIndex.deleteOffset || config.cron.deleteOverMaxOffset))){
const maxSize = (storageIndex.sizeLimit * (storageIndex.videoPercent / 100) * config.cron.deleteOverMaxOffset);
if(storageIndex.usedSpaceVideos > maxSize){
s.knexQuery({
action: "select",
columns: "*",
@ -242,19 +237,62 @@ module.exports = (s,config,lang) => {
}
readStorageArray()
}
const deleteAddStorageTimelapseFrames = function(groupKey,callback){
const theGroup = s.group[groupKey]
reRunCheck = function(){
s.debugLog('deleteAddStorageTimelapseFrames')
return deleteAddStorageTimelapseFrames(groupKey,callback)
}
var currentStorageNumber = 0
function readStorageArray(){
setTimeout(function(){
reRunCheck = readStorageArray
var storage = s.listOfStorage[currentStorageNumber]
if(!storage){
//done all checks, move on to next user
callback()
return
}
var storageId = storage.value
if(storageId === '' || !theGroup.addStorageUse[storageId]){
++currentStorageNumber
readStorageArray()
return
}
var storageIndex = theGroup.addStorageUse[storageId]
//run purge command
const maxSize = (storageIndex.sizeLimit * (storageIndex.timelapsePercent / 100) * config.cron.deleteOverMaxOffset);
if(storageIndex.usedSpaceTimelapseFrames > maxSize){
s.knexQuery({
action: "select",
columns: "*",
table: "Timelapse Frames",
where: [
['ke','=',groupKey],
['details','LIKE',`%"dir":"${storage.value}"%`],
],
orderBy: ['time','asc'],
limit: 3
},(err,frames) => {
deleteSetOfTimelapseFrames({
groupKey: groupKey,
err: err,
frames: frames,
storageIndex: storageIndex,
reRunCheck: () => {
return readStorageArray()
}
},callback)
})
}else{
++currentStorageNumber
readStorageArray()
}
})
}
readStorageArray()
}
const deleteMainVideos = function(groupKey,callback){
// //run purge command
// s.debugLog('!!!!!!!!!!!deleteMainVideos')
// s.debugLog('s.group[groupKey].usedSpaceVideos > (s.group[groupKey].sizeLimit * (s.group[groupKey].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset)')
// s.debugLog(s.group[groupKey].usedSpaceVideos > (s.group[groupKey].sizeLimit * (s.group[groupKey].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset))
// s.debugLog('s.group[groupKey].usedSpaceVideos')
// s.debugLog(s.group[groupKey].usedSpaceVideos)
// s.debugLog('s.group[groupKey].sizeLimit * (s.group[groupKey].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset')
// s.debugLog(s.group[groupKey].sizeLimit * (s.group[groupKey].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset)
// s.debugLog('s.group[groupKey].sizeLimitVideoPercent / 100')
// s.debugLog(s.group[groupKey].sizeLimitVideoPercent / 100)
// s.debugLog('s.group[groupKey].sizeLimit')
// s.debugLog(s.group[groupKey].sizeLimit)
if(s.group[groupKey].usedSpaceVideos > (s.group[groupKey].sizeLimit * (s.group[groupKey].sizeLimitVideoPercent / 100) * config.cron.deleteOverMaxOffset)){
s.knexQuery({
action: "select",
@ -295,7 +333,7 @@ module.exports = (s,config,lang) => {
table: "Timelapse Frames",
where: [
['ke','=',groupKey],
['archive','!=',`1`],
['details','NOT LIKE',`%"dir"%`],
],
orderBy: ['time','asc'],
limit: 3
@ -508,6 +546,7 @@ module.exports = (s,config,lang) => {
deleteAddStorageVideos: deleteAddStorageVideos,
deleteMainVideos: deleteMainVideos,
deleteTimelapseFrames: deleteTimelapseFrames,
deleteAddStorageTimelapseFrames,
deleteFileBinFiles: deleteFileBinFiles,
deleteCloudVideos: deleteCloudVideos,
deleteCloudTimelapseFrames: deleteCloudTimelapseFrames,

View File

@ -1231,7 +1231,7 @@ module.exports = function(s,config,lang,app,io){
['mid','=',req.params.id],
],
limit: 1
},(err,r) => {
},async (err,r) => {
if(r && r[0]){
r = r[0];
if(req.query.reset==='1'||(s.group[r.ke]&&s.group[r.ke].rawMonitorConfigurations[r.mid].mode!==req.params.f)||req.query.fps&&(!s.group[r.ke].activeMonitors[r.mid].currentState||!s.group[r.ke].activeMonitors[r.mid].currentState.trigger_on)){
@ -1261,7 +1261,7 @@ module.exports = function(s,config,lang,app,io){
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
s.camera('stop',s.cleanMonitorObject(r));
await s.camera('stop',s.cleanMonitorObject(r));
if(req.params.f!=='stop'){
s.camera(req.params.f,s.cleanMonitorObject(r));
}
@ -1288,7 +1288,7 @@ module.exports = function(s,config,lang,app,io){
req.timeout=req.params.ff*1000
break;
}
s.group[r.ke].activeMonitors[r.mid].trigger_timer=setTimeout(function(){
s.group[r.ke].activeMonitors[r.mid].trigger_timer=setTimeout(async function(){
delete(s.group[r.ke].activeMonitors[r.mid].trigger_timer)
s.knexQuery({
action: "update",
@ -1304,12 +1304,11 @@ module.exports = function(s,config,lang,app,io){
r.neglectTriggerTimer=1;
r.mode=s.group[r.ke].activeMonitors[r.mid].currentState.mode;
r.fps=s.group[r.ke].activeMonitors[r.mid].currentState.fps;
s.camera('stop',s.cleanMonitorObject(r),function(){
if(s.group[r.ke].activeMonitors[r.mid].currentState.mode!=='stop'){
s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
}
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
});
await s.camera('stop',s.cleanMonitorObject(r));
if(s.group[r.ke].activeMonitors[r.mid].currentState.mode!=='stop'){
s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
}
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
},req.timeout);

View File

@ -69,7 +69,7 @@ module.exports = function(s,config,lang,app){
define: s.getDefinitonFile(user.details ? user.details.lang : config.lang),
lang: lang,
$user: $user,
mon: Object.assign(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{}),
mon: Object.assign({},s.group[req.params.ke].rawMonitorConfigurations[req.params.id]),
originalURL: s.getOriginalUrl(req)
});
}else{

View File

@ -10,14 +10,22 @@ $(document).ready(function(){
var addStorageData = JSON.parse($user.details.addStorage || '{}')
var html = ''
$.each(addStorage,function(n,storage){
var limit = ""
if(addStorageData[storage.path] && addStorageData[storage.path].limit){
limit = addStorageData[storage.path].limit
}
html += `<div class="form-group">
<div class="mb-2"><span>${lang['Max Storage Amount']} : ${storage.name}</span></div>
<div><input class="form-control" placeholder="10000" addStorageLimit="${storage.path}" value="${limit}"></div>
</div>`
var theStorage = addStorageData[storage.path]
html += `
<div addStorageFields="${storage.path}">
<div class="form-group">
<div class="mb-2"><span>${lang['Max Storage Amount']} : ${storage.name}</span></div>
<div><input class="form-control" placeholder="10000" addStorageItem="limit" value="${theStorage.limit || ''}"></div>
</div>
<div class="form-group">
<div class="mb-2"><span>${lang["Video Share"]} : ${storage.name}</span></div>
<div><input class="form-control" placeholder="95" addStorageItem="videoPercent" value="${theStorage.videoPercent || ''}"></div>
</div>
<div class="form-group">
<div class="mb-2"><span>${lang["Timelapse Frames Share"]} : ${storage.name}</span></div>
<div><input class="form-control" placeholder="5" addStorageItem="timelapsePercent" value="${theStorage.timelapsePercent || ''}"></div>
</div>
</div>`
})
addStorageMaxAmounts.html(html)
}catch(err){
@ -35,20 +43,25 @@ $(document).ready(function(){
extender(theForm)
})
}
addStorageMaxAmounts.on('change','[addStorageLimit]',function(){
function getAddStorageFields(){
var json = {}
$.each(addStorage,function(n,storage){
var storageId = storage.path
var el = addStorageMaxAmounts.find('[addStorageLimit="' + storageId + '"]')
var value = el.val()
var miniContainer = addStorageMaxAmounts.find(`[addStorageFields="${storageId}"]`)
var fields = miniContainer.find('[addStorageItem]')
json[storageId] = {
name: storage.name,
path: storage.path,
limit: value
}
$.each(fields,function(n,el){
var field = $(el)
var keyName = field.attr('addStorageItem')
var value = field.val()
json[storageId][keyName] = value
})
})
addStorageMaxAmountsField.val(JSON.stringify(json))
})
return json
}
$('body')
theForm.find('[detail]').change(onDetailFieldChange)
theForm.find('[detail]').change(function(){
@ -77,6 +90,7 @@ $(document).ready(function(){
})
var details = getDetailValues(theForm)
formData.details = details
formData.details.addStorage = getAddStorageFields()
accountSettings.onSaveFieldsExtensions.forEach(function(extender){
extender(formData)
})

View File

@ -911,9 +911,13 @@ function drawIndicatorBar(item){
</div>
</div>
<div>
<div class="progress">
${!item.multiple ? `<div class="progress">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: 0%;"></div>
</div>
</div>` : `<div class="progress">
<div class="progress-bar progress-bar-info" role="progressbar" style="width: 0%;"></div>
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: 0%;"></div>
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: 0%;"></div>
</div>`}
</div>
</div>
</div>`

View File

@ -757,6 +757,10 @@ monitorEditorWindow.on('change','[detail="auto_host"]',function(e){
}
})
editorForm.submit(function(e){
function setSubmitButton(text,icon,toggle){
var submitButtons = editorForm.find('[type="submit"]').prop('disabled',toggle)
submitButtons.html(`<i class="fa fa-${icon}"></i> ${text}`)
}
e.preventDefault();
var validation = getMonitorEditFormFields()
if(!validation.ok){
@ -765,6 +769,7 @@ editorForm.submit(function(e){
new PNotify({title:'Configuration Invalid',text:errorsFound.join('<br>'),type:'error'});
}
var monitorConfig = validation.monitorConfig
setSubmitButton(lang[`Please Wait...`], `spinner fa-pulse`, true)
$.post(getApiPrefix()+'/configureMonitor/'+$user.ke+'/'+monitorConfig.mid,{data:JSON.stringify(monitorConfig)},function(d){
if(d.ok === false){
new PNotify({
@ -774,6 +779,7 @@ editorForm.submit(function(e){
})
}
debugLog(d)
setSubmitButton(lang.Save, `check`, false)
})
//
if(copySettingsSelector.val() === '1'){

View File

@ -45,61 +45,6 @@ $(document).ready(function(){
</div>
</div>`)
}
function createMonitorVideosTab(monitor){
var startDate = moment().subtract(32, 'hour').utc()
var endDate = moment().add(1, 'hour').utc()
var newTabId = `monitorVideos-${monitor.mid}`
var tabLabel = `<b>${lang['Videos']}</b> : ${monitor.name}`
var baseHtml = `<main class="container page-tab" id="tab-${newTabId}">
<div class="my-3 p-3 ${definitions.Theme.isDark ? 'bg-dark text-white' : 'bg-light text-dark'} rounded shadow-sm">
<h6 class="border-bottom-dotted border-bottom-dark pb-2 mb-0 row">
<div class="col-md-8">${lang['Videos']} : ${monitor.name}</div>
<div class="col-md-4"><input class="form-control form-control-sm btn-dark text-md-end text-sm-center" type="text" id="daterange-${newTabId}" value="01/01/2018 - 01/15/2018" /></div>
</h6>
<div class="video-list flex-table flex-table-dark mx-n3 pt-2 px-3 row">
</div>
</div>
</main>`
createNewTab(newTabId,tabLabel,baseHtml,{},null,'videosList')
getVideos({
monitorId: monitor.mid,
startDate: startDate._d,
endDate: endDate._d,
},function(data){
var videos = data.videos
if(videos.length === 0){
getVideos({
monitorId: monitor.mid,
limit: 20,
},function(data){
var videos = data.videos
drawVideoRowsToList(`#tab-monitorVideos-${monitor.mid} .video-list`,videos)
})
}else{
drawVideoRowsToList(`#tab-monitorVideos-${monitor.mid} .video-list`,videos)
}
})
$(`#daterange-${newTabId}`).daterangepicker({
timePicker: true,
startDate: startDate,
endDate: endDate,
locale: {
format: 'YYYY-MM-DD hh:mm:ss A'
}
}, function(start, end, label) {
var startDate = start.clone().utc()
var endDate = end.clone().utc()
getVideos({
monitorId: monitor.mid,
startDate: startDate._d,
endDate: endDate._d,
},function(data){
var videos = data.videos
drawVideoRowsToList(`#tab-monitorVideos-${monitor.mid} .video-list`,videos)
})
})
}
function loadMonitorsFromMemory(options,callback){
theList.empty();
$.each(getLoadedMonitorsAlphabetically(),function(n,row){
@ -168,12 +113,6 @@ $(document).ready(function(){
console.log(data)
})
})
.on('click','.open-videos',function(){
var monitorId = getRowsMonitorId(this)
var monitor = loadedMonitors[monitorId]
createMonitorVideosTab(monitor)
console.log(monitorId)
})
.on('click','.export-this-monitor-settings',function(){
var monitorId = getRowsMonitorId(this)
downloadMonitorConfigurationsToDisk([

View File

@ -91,6 +91,7 @@ function loadBoxWrappers() {
function drawAddStorageIndicators(){
$.each(addStorage,function(n,storage){
drawIndicatorBar({
multiple: true,
icon: 'hdd-o',
name: storage.name,
label: `<span style="text-transform:capitalize">${storage.name}</span> : <span class="value"></span>`,
@ -199,16 +200,25 @@ onWebSocketEvent(function (d){
diskIndicatorBar[2].title = `${lang['FileBin Share']} : ${fileBinPercent}`
if(d.addStorage){
$.each(d.addStorage,function(n,storage){
var percent = parseInt((storage.usedSpace/storage.sizeLimit)*100)+'%'
var diskIndicator = loadedIndicators[storage.name]
var diskIndicatorBars = diskIndicator.progressBar
var diskLimit = storage.sizeLimit
var percent = parseDiskUsePercent(storage.usedSpace,diskLimit);
var videosPercent = parseDiskUsePercent(storage.usedSpaceVideos,diskLimit);
var timelapsePercent = parseDiskUsePercent(storage.usedSpaceTimelapseFrames,diskLimit);
//
var humanValue = parseFloat(storage.usedSpace)
if(humanValue > 1000){
humanValue = (humanValue/1000).toFixed(2)+' GB'
}else{
humanValue = humanValue.toFixed(2)+' MB'
}
loadedIndicators[storage.name].value.html(humanValue)
loadedIndicators[storage.name].percent.html(percent)
loadedIndicators[storage.name].progressBar.css('width',percent)
diskIndicator.value.html(humanValue)
diskIndicator.percent.html(percent)
diskIndicatorBars[0].style.width = videosPercent
diskIndicatorBars[0].title = `${lang['Video Share']} : ${videosPercent}`
diskIndicatorBars[1].style.width = timelapsePercent
diskIndicatorBars[1].title = `${lang['Timelapse Frames Share']} : ${timelapsePercent}`
})
}
break;

View File

@ -457,7 +457,9 @@ function getVideos(options,callback){
var monitorId = options.monitorId
var archived = options.archived
var customVideoSet = options.customVideoSet
var limit = options.limit || 300
var limit = options.limit
var eventLimit = options.eventLimit || 300
var doLimitOnFames = options.doLimitOnFames || false
var eventStartTime
var eventEndTime
// var startDate = options.startDate
@ -476,14 +478,14 @@ function getVideos(options,callback){
if(archived){
requestQueries.push(`archived=1`)
}
$.getJSON(`${getApiPrefix(customVideoSet ? customVideoSet : searchQuery ? `videosByEventTag` : `videos`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(data){
$.getJSON(`${getApiPrefix(customVideoSet ? customVideoSet : searchQuery ? `videosByEventTag` : `videos`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([limit ? `limit=${limit}` : `noLimit=1`]).join('&')}`,function(data){
var videos = data.videos.map((video) => {
return Object.assign({},video,{
href: getFullOrigin(true) + video.href
})
})
$.getJSON(`${getApiPrefix(`timelapse`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(timelapseFrames){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${eventLimit}`]).join('&')}`,function(eventData){
var theEvents = eventData.events || eventData;
var newVideos = applyDataListToVideos(videos,theEvents)
newVideos = applyTimelapseFramesListToVideos(newVideos,timelapseFrames.frames || timelapseFrames,'timelapseFrames',true).map((video) => {