Bug Fix Bandit
parent
4979b1d57e
commit
f41fe9ecf6
|
@ -725,6 +725,15 @@ module.exports = function(s,config,lang){
|
||||||
"form-group-class": "h_gpud_input h_gpud_1",
|
"form-group-class": "h_gpud_input h_gpud_1",
|
||||||
"possible": ""
|
"possible": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "detail=hwaccel_format",
|
||||||
|
"field": lang.hwaccel_format,
|
||||||
|
"description": "Hardware Acceleration Format",
|
||||||
|
"default": "",
|
||||||
|
"example": "vaapi",
|
||||||
|
"form-group-class": "h_gpud_input h_gpud_1",
|
||||||
|
"possible": ""
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Input Maps": {
|
"Input Maps": {
|
||||||
|
@ -4931,26 +4940,6 @@ module.exports = function(s,config,lang){
|
||||||
"placeholder": "3",
|
"placeholder": "3",
|
||||||
attribute:'localStorage="montage"',
|
attribute:'localStorage="montage"',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"field": lang['Cycle Monitors per row'],
|
|
||||||
"placeholder": "2",
|
|
||||||
attribute:'localStorage="cycleLivePerRow"',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": lang['Number of Cycle Monitors'],
|
|
||||||
"placeholder": "4",
|
|
||||||
attribute:'localStorage="cycleLiveNumberOfMonitors"',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": lang['Cycle Monitor Height'],
|
|
||||||
"placeholder": "4",
|
|
||||||
attribute:'localStorage="cycleLiveMonitorHeight"',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": lang['Cycle Interval'],
|
|
||||||
"placeholder": "30000",
|
|
||||||
attribute:'localStorage="cycleLiveTimerAmount"',
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Preferences": {
|
"Preferences": {
|
||||||
|
@ -7723,12 +7712,6 @@ module.exports = function(s,config,lang){
|
||||||
attributes: 'shinobi-switch="monitorMuteAudio" ui-change-target=".dot" on-class="dot-green" off-class="dot-grey"',
|
attributes: 'shinobi-switch="monitorMuteAudio" ui-change-target=".dot" on-class="dot-green" off-class="dot-grey"',
|
||||||
color: 'grey',
|
color: 'grey',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: lang['Cycle Monitors'],
|
|
||||||
class: 'cursor-pointer',
|
|
||||||
attributes: 'shinobi-switch="cycleLiveGrid" ui-change-target=".dot" on-class="dot-green" off-class="dot-grey"',
|
|
||||||
color: 'grey',
|
|
||||||
},
|
|
||||||
// {
|
// {
|
||||||
// label: lang['JPEG Mode'],
|
// label: lang['JPEG Mode'],
|
||||||
// class: 'cursor-pointer',
|
// class: 'cursor-pointer',
|
||||||
|
|
|
@ -385,6 +385,7 @@
|
||||||
"Started": "Gestartet",
|
"Started": "Gestartet",
|
||||||
"Status Indicator": "Statusanzeige",
|
"Status Indicator": "Statusanzeige",
|
||||||
"Stop URL": "Stop-URL",
|
"Stop URL": "Stop-URL",
|
||||||
|
"Storage Class": "Speicherklassen",
|
||||||
"Stream": "Stream",
|
"Stream": "Stream",
|
||||||
"Stream Flags": "Stream-Flags",
|
"Stream Flags": "Stream-Flags",
|
||||||
"Stream Timestamp": "Stream-Timestamp",
|
"Stream Timestamp": "Stream-Timestamp",
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
"Session Key": "Session Key",
|
"Session Key": "Session Key",
|
||||||
"Active Monitors": "Active Monitors",
|
"Active Monitors": "Active Monitors",
|
||||||
"Storage Use": "Storage Use",
|
"Storage Use": "Storage Use",
|
||||||
|
"Storage Class": "Storage Class",
|
||||||
"Use Raw Snapshot": "Use Raw Snapshot",
|
"Use Raw Snapshot": "Use Raw Snapshot",
|
||||||
"Failed to Edit Account": "Failed to Edit Account",
|
"Failed to Edit Account": "Failed to Edit Account",
|
||||||
"How to Connect": "How to Connect",
|
"How to Connect": "How to Connect",
|
||||||
|
@ -635,6 +636,7 @@
|
||||||
"NoVideosFoundForDateRange": "No Videos found in this date range. Try setting the start date further back.",
|
"NoVideosFoundForDateRange": "No Videos found in this date range. Try setting the start date further back.",
|
||||||
"NoLogsFoundForDateRange": "No Logs found in this date range. Try widening the date range.",
|
"NoLogsFoundForDateRange": "No Logs found in this date range. Try widening the date range.",
|
||||||
"monitorEditFailedMaxReached": "Your account has reached the maximum number of cameras that can be created. Speak to an administrator if you would like this changed.",
|
"monitorEditFailedMaxReached": "Your account has reached the maximum number of cameras that can be created. Speak to an administrator if you would like this changed.",
|
||||||
|
"monitorEditFailedMaxReachedUnactivated": "Your system has reached the maximum number of cameras that can be created. You must activate your installation to create more.",
|
||||||
"Sub-Accounts": "Sub-Accounts",
|
"Sub-Accounts": "Sub-Accounts",
|
||||||
"Stream in Background": "Stream in Background",
|
"Stream in Background": "Stream in Background",
|
||||||
"Carousel in Background": "Carousel in Background",
|
"Carousel in Background": "Carousel in Background",
|
||||||
|
@ -1161,6 +1163,7 @@
|
||||||
"startUpText3": "waiting to give unfinished video check some time. 3 seconds.",
|
"startUpText3": "waiting to give unfinished video check some time. 3 seconds.",
|
||||||
"startUpText4": "Starting Monitors... Please Wait...",
|
"startUpText4": "Starting Monitors... Please Wait...",
|
||||||
"startUpText5": "Shinobi is ready.",
|
"startUpText5": "Shinobi is ready.",
|
||||||
|
"notReadyYet": "Shinobi is not ready yet to do this.",
|
||||||
"startUpText6": "Orphaned Videos Found and Inserted",
|
"startUpText6": "Orphaned Videos Found and Inserted",
|
||||||
"Migrator": "Migrator",
|
"Migrator": "Migrator",
|
||||||
"Thumbnail": "Thumbnail",
|
"Thumbnail": "Thumbnail",
|
||||||
|
@ -1269,6 +1272,7 @@
|
||||||
"hwaccel": "Acceleration Engine",
|
"hwaccel": "Acceleration Engine",
|
||||||
"hwaccel_vcodec": "Video Decoder",
|
"hwaccel_vcodec": "Video Decoder",
|
||||||
"hwaccel_device": "HWAccel Device",
|
"hwaccel_device": "HWAccel Device",
|
||||||
|
"hwaccel_format": "HWAccel Format",
|
||||||
"Get Logs to Client": "Get Logs to Client",
|
"Get Logs to Client": "Get Logs to Client",
|
||||||
"Hardware Accelerated": "Hardware Accelerated",
|
"Hardware Accelerated": "Hardware Accelerated",
|
||||||
"Accelerator": "Accelerator",
|
"Accelerator": "Accelerator",
|
||||||
|
|
|
@ -504,6 +504,7 @@
|
||||||
"Stop": "Arrêt",
|
"Stop": "Arrêt",
|
||||||
"Stop URL": "URL d'arrêt",
|
"Stop URL": "URL d'arrêt",
|
||||||
"Storage Location": "Emplacement de stockage",
|
"Storage Location": "Emplacement de stockage",
|
||||||
|
"Storage Class": "Classes de Stockage",
|
||||||
"Stream": "Flux",
|
"Stream": "Flux",
|
||||||
"Stream Channel": "Canal du flux de données",
|
"Stream Channel": "Canal du flux de données",
|
||||||
"Stream Flags": "Etiquettes du flux",
|
"Stream Flags": "Etiquettes du flux",
|
||||||
|
|
|
@ -943,6 +943,7 @@
|
||||||
"Stopping": "Fermarsi",
|
"Stopping": "Fermarsi",
|
||||||
"Storage Location": "Posizione di archiviazione",
|
"Storage Location": "Posizione di archiviazione",
|
||||||
"Storage Use": "Uso di archiviazione",
|
"Storage Use": "Uso di archiviazione",
|
||||||
|
"Storage Class": "Classi di Archiviazione",
|
||||||
"Stream": "Flusso",
|
"Stream": "Flusso",
|
||||||
"Stream Channel": "Canale di flusso",
|
"Stream Channel": "Canale di flusso",
|
||||||
"Stream Flags": "Flag di streaming",
|
"Stream Flags": "Flag di streaming",
|
||||||
|
|
|
@ -1489,6 +1489,7 @@
|
||||||
"Stopping": "停止中",
|
"Stopping": "停止中",
|
||||||
"Storage Location": "Storage Location",
|
"Storage Location": "Storage Location",
|
||||||
"Storage Use": "使用ストレージ",
|
"Storage Use": "使用ストレージ",
|
||||||
|
"Storage Class": "ストレージクラス",
|
||||||
"Stream Channel": "Stream Channel",
|
"Stream Channel": "Stream Channel",
|
||||||
"Stream Channels": "Stream Channels",
|
"Stream Channels": "Stream Channels",
|
||||||
"Stream Flags": "ストリームフラグ",
|
"Stream Flags": "ストリームフラグ",
|
||||||
|
|
|
@ -169,7 +169,7 @@ module.exports = function(s,config,lang){
|
||||||
activeSession &&
|
activeSession &&
|
||||||
(
|
(
|
||||||
activeSession.ip.indexOf('0.0.0.0') > -1 ||
|
activeSession.ip.indexOf('0.0.0.0') > -1 ||
|
||||||
params.ip.indexOf(activeSession.ip) > -1
|
params.ip && (params.ip.indexOf(activeSession.ip) > -1)
|
||||||
)
|
)
|
||||||
){
|
){
|
||||||
if(!user.lang){
|
if(!user.lang){
|
||||||
|
@ -218,6 +218,13 @@ module.exports = function(s,config,lang){
|
||||||
onFail()
|
onFail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.authPromise = function(params,res,req){
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
s.auth(params, (user) => {
|
||||||
|
resolve(user)
|
||||||
|
},res,req)
|
||||||
|
})
|
||||||
|
}
|
||||||
//super user authentication handler
|
//super user authentication handler
|
||||||
s.superAuth = function(params,callback,res,req){
|
s.superAuth = function(params,callback,res,req){
|
||||||
var userFound = false
|
var userFound = false
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
const { AbortController } = require('node-abort-controller')
|
||||||
|
module.exports = (s,config,lang) => {
|
||||||
|
const fetchTimeout = (url, ms, { signal, ...options } = {}) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const promise = fetch(url, { signal: controller.signal, ...options });
|
||||||
|
if (signal) signal.addEventListener("abort", () => controller.abort());
|
||||||
|
const timeout = setTimeout(() => controller.abort(), ms);
|
||||||
|
return promise.finally(() => clearTimeout(timeout));
|
||||||
|
}
|
||||||
|
function canAddMoreMonitors() {
|
||||||
|
const cameraCountChecks = [
|
||||||
|
{ kind: 'ec2', maxCameras: 2, condition: config.isEC2 },
|
||||||
|
{ kind: 'highCoreCount', maxCameras: 50, condition: config.isHighCoreCount },
|
||||||
|
{ kind: 'default', maxCameras: 30, condition: true },
|
||||||
|
];
|
||||||
|
if (!config.userHasSubscribed) {
|
||||||
|
const monitorCountOnSystem = getTotalMonitorCount();
|
||||||
|
for (const check of cameraCountChecks) {
|
||||||
|
if (check.condition && monitorCountOnSystem >= check.maxCameras) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function getTotalMonitorCount() {
|
||||||
|
let monitorCount = 0;
|
||||||
|
try{
|
||||||
|
for (const groupKey in s.group) {
|
||||||
|
const monitorIds = Object.keys(s.group[groupKey].rawMonitorConfigurations);
|
||||||
|
monitorCount += monitorIds.length;
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
s.debugLog(err)
|
||||||
|
}
|
||||||
|
return monitorCount;
|
||||||
|
}
|
||||||
|
function sanitizeMonitorConfig(monitorConfig){
|
||||||
|
const sanitized = {}
|
||||||
|
const errors = {}
|
||||||
|
const availableKeys = [
|
||||||
|
{name: 'ke', type: 'string'},
|
||||||
|
{name: 'mid', type: 'string'},
|
||||||
|
{name: 'name', type: 'string'},
|
||||||
|
{name: 'details', type: 'longtext'},
|
||||||
|
{name: 'type', type: 'string', defaultTo: 'h264'},
|
||||||
|
{name: 'ext', type: 'string', defaultTo: 'mp4'},
|
||||||
|
{name: 'protocol', type: 'string', defaultTo: 'rtsp'},
|
||||||
|
{name: 'host', type: 'string', missingHalt: true },
|
||||||
|
{name: 'path', type: 'string', missingHalt: true },
|
||||||
|
{name: 'port', type: 'integer', defaultTo: 554},
|
||||||
|
{name: 'fps', type: 'integer', defaultTo: null},
|
||||||
|
{name: 'mode', type: 'string', defaultTo: 'stop'},
|
||||||
|
{name: 'width', type: 'integer', defaultTo: 640},
|
||||||
|
{name: 'height', type: 'integer', defaultTo: 480},
|
||||||
|
];
|
||||||
|
for(item of availableKeys){
|
||||||
|
const column = item.name;
|
||||||
|
const type = item.type;
|
||||||
|
const monitorValue = monitorConfig[column]
|
||||||
|
let newValue = monitorValue;
|
||||||
|
switch(type){
|
||||||
|
case'string':
|
||||||
|
case'longtext':
|
||||||
|
if(monitorValue instanceof String){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
newValue = `${monitorValue}`;
|
||||||
|
errors[column] = `corrected ${type} type : ${typeof monitorValue}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case'integer':
|
||||||
|
if(!isNaN(monitorValue)){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
newValue = parseInt(monitorValue);
|
||||||
|
errors[column] = `corrected ${type} type : ${typeof monitorValue}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sanitized[column] = newValue;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sanitized,
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isGroupBelowMaxMonitorCount(groupKey){
|
||||||
|
const theGroup = s.group[groupKey];
|
||||||
|
try{
|
||||||
|
const initData = theGroup.init;
|
||||||
|
const maxCamerasAllowed = parseInt(initData.max_camera) || false;
|
||||||
|
return (!maxCamerasAllowed || Object.keys(theGroup.activeMonitors).length <= parseInt(maxCamerasAllowed))
|
||||||
|
}catch(err){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
fetchTimeout,
|
||||||
|
canAddMoreMonitors,
|
||||||
|
getTotalMonitorCount,
|
||||||
|
sanitizeMonitorConfig,
|
||||||
|
isGroupBelowMaxMonitorCount,
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ module.exports = function(s){
|
||||||
try{
|
try{
|
||||||
var config = require(s.location.config)
|
var config = require(s.location.config)
|
||||||
}catch(err){
|
}catch(err){
|
||||||
|
console.log('FAILED TO OPEN CONFIGURATION FILE')
|
||||||
|
console.log('CHECK SYNTAX!')
|
||||||
var config = {}
|
var config = {}
|
||||||
}
|
}
|
||||||
if(!config.productType){
|
if(!config.productType){
|
||||||
|
|
15
libs/cron.js
15
libs/cron.js
|
@ -28,9 +28,24 @@ module.exports = (s,config,lang) => {
|
||||||
case's.deleteVideo':
|
case's.deleteVideo':
|
||||||
s.deleteVideo(data.file)
|
s.deleteVideo(data.file)
|
||||||
break;
|
break;
|
||||||
|
case's.deleteCloudVideo':
|
||||||
|
s.deleteVideo(data.file)
|
||||||
|
break;
|
||||||
case's.deleteFileBinEntry':
|
case's.deleteFileBinEntry':
|
||||||
s.deleteFileBinEntry(data.file)
|
s.deleteFileBinEntry(data.file)
|
||||||
break;
|
break;
|
||||||
|
case's.onCronGroupBeforeProcessed':
|
||||||
|
s.runExtensionsForArray('onCronGroupBeforeProcessed', null, data.args)
|
||||||
|
break;
|
||||||
|
case's.onCronGroupBeforeProcessedAwaited':
|
||||||
|
s.runExtensionsForArrayAwaited('onCronGroupBeforeProcessedAwaited', null, data.args)
|
||||||
|
break;
|
||||||
|
case's.onCronGroupProcessed':
|
||||||
|
s.runExtensionsForArray('onCronGroupProcessed', null, data.args)
|
||||||
|
break;
|
||||||
|
case's.onCronGroupProcessedAwaited':
|
||||||
|
s.runExtensionsForArrayAwaited('onCronGroupProcessedAwaited', null, data.args)
|
||||||
|
break;
|
||||||
case's.setDiskUsedForGroup':
|
case's.setDiskUsedForGroup':
|
||||||
function doOnMain(){
|
function doOnMain(){
|
||||||
s.setDiskUsedForGroup(data.ke,data.size,data.target || undefined)
|
s.setDiskUsedForGroup(data.ke,data.size,data.target || undefined)
|
||||||
|
|
|
@ -4,7 +4,7 @@ const moment = require('moment');
|
||||||
const exec = require('child_process').exec;
|
const exec = require('child_process').exec;
|
||||||
const spawn = require('child_process').spawn;
|
const spawn = require('child_process').spawn;
|
||||||
const { parentPort, isMainThread, workerData } = require('worker_threads');
|
const { parentPort, isMainThread, workerData } = require('worker_threads');
|
||||||
const config = workerData
|
const config = workerData;
|
||||||
process.on('uncaughtException', function (err) {
|
process.on('uncaughtException', function (err) {
|
||||||
errorLog('uncaughtException',err);
|
errorLog('uncaughtException',err);
|
||||||
});
|
});
|
||||||
|
@ -96,6 +96,18 @@ function beginProcessing(){
|
||||||
const deleteFileBinEntry = (x) => {
|
const deleteFileBinEntry = (x) => {
|
||||||
postMessage({f:'s.deleteFileBinEntry',file:x})
|
postMessage({f:'s.deleteFileBinEntry',file:x})
|
||||||
}
|
}
|
||||||
|
const onCronGroupBeforeProcessed = (...args) => {
|
||||||
|
postMessage({f:'s.onCronGroupBeforeProcessed', args: args})
|
||||||
|
}
|
||||||
|
const onCronGroupBeforeProcessedAwaited = (...args) => {
|
||||||
|
postMessage({f:'s.onCronGroupBeforeProcessedAwaited', args: args})
|
||||||
|
}
|
||||||
|
const onCronGroupProcessed = (...args) => {
|
||||||
|
postMessage({f:'s.onCronGroupProcessed', args: args})
|
||||||
|
}
|
||||||
|
const onCronGroupProcessedAwaited = (...args) => {
|
||||||
|
postMessage({f:'s.onCronGroupProcessedAwaited', args: args})
|
||||||
|
}
|
||||||
const setDiskUsedForGroup = (groupKey,size,target,videoRow) => {
|
const setDiskUsedForGroup = (groupKey,size,target,videoRow) => {
|
||||||
postMessage({f:'s.setDiskUsedForGroup', ke: groupKey, size: size, target: target, videoRow: videoRow})
|
postMessage({f:'s.setDiskUsedForGroup', ke: groupKey, size: size, target: target, videoRow: videoRow})
|
||||||
}
|
}
|
||||||
|
@ -546,6 +558,9 @@ function beginProcessing(){
|
||||||
overlapLocks[v.ke] = true
|
overlapLocks[v.ke] = true
|
||||||
v.d = JSON.parse(v.details);
|
v.d = JSON.parse(v.details);
|
||||||
try{
|
try{
|
||||||
|
debugLog('--- Running Pre Extenders')
|
||||||
|
onCronGroupBeforeProcessed(v)
|
||||||
|
onCronGroupBeforeProcessedAwaited(v)
|
||||||
await deleteOldVideos(v)
|
await deleteOldVideos(v)
|
||||||
debugLog('--- deleteOldVideos Complete')
|
debugLog('--- deleteOldVideos Complete')
|
||||||
await deleteOldTimelapseFrames(v)
|
await deleteOldTimelapseFrames(v)
|
||||||
|
@ -562,6 +577,9 @@ function beginProcessing(){
|
||||||
debugLog('--- checkFilterRules Complete')
|
debugLog('--- checkFilterRules Complete')
|
||||||
await deleteRowsWithNoVideo(v)
|
await deleteRowsWithNoVideo(v)
|
||||||
debugLog('--- deleteRowsWithNoVideo Complete')
|
debugLog('--- deleteRowsWithNoVideo Complete')
|
||||||
|
debugLog('--- Running Post Extenders')
|
||||||
|
onCronGroupProcessed(v)
|
||||||
|
onCronGroupProcessedAwaited(v)
|
||||||
}catch(err){
|
}catch(err){
|
||||||
normalLog(`Failed to Complete User : ${v.mail}`)
|
normalLog(`Failed to Complete User : ${v.mail}`)
|
||||||
normalLog(err)
|
normalLog(err)
|
||||||
|
|
|
@ -175,9 +175,9 @@ module.exports = function(s,config){
|
||||||
}
|
}
|
||||||
const getDatabaseRows = function(options,callback){
|
const getDatabaseRows = function(options,callback){
|
||||||
//current cant handle `end` time
|
//current cant handle `end` time
|
||||||
var whereQuery = [
|
var whereQuery = options.groupKey ? [
|
||||||
['ke','=',options.groupKey],
|
['ke','=',options.groupKey],
|
||||||
]
|
] : []
|
||||||
const monitorRestrictions = options.monitorRestrictions
|
const monitorRestrictions = options.monitorRestrictions
|
||||||
var frameLimit = options.limit
|
var frameLimit = options.limit
|
||||||
const noLimit = options.noLimit === '1'
|
const noLimit = options.noLimit === '1'
|
||||||
|
@ -482,6 +482,36 @@ module.exports = function(s,config){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const dateSubtract = function(date, interval, units){
|
||||||
|
var ret = date
|
||||||
|
var checkRollover = function() { if(ret.getDate() != date.getDate()) ret.setDate(0);};
|
||||||
|
switch(interval.toLowerCase()) {
|
||||||
|
case 'year' : ret.setFullYear(ret.getFullYear() - units); checkRollover(); break;
|
||||||
|
case 'quarter': ret.setMonth(ret.getMonth() - 3*units); checkRollover(); break;
|
||||||
|
case 'month' : ret.setMonth(ret.getMonth() - units); checkRollover(); break;
|
||||||
|
case 'week' : ret.setDate(ret.getDate() - 7*units); break;
|
||||||
|
case 'day' : ret.setDate(ret.getDate() - units); break;
|
||||||
|
case 'hour' : ret.setTime(ret.getTime() - units*3600000); break;
|
||||||
|
case 'minute' : ret.setTime(ret.getTime() - units*60000); break;
|
||||||
|
case 'second' :default: ret.setTime(ret.getTime() - units*1000); break;
|
||||||
|
}
|
||||||
|
return (new Date(ret))
|
||||||
|
}
|
||||||
|
const sqlDate = function(value){
|
||||||
|
var value = value.toLowerCase()
|
||||||
|
var splitValue = value.split(' ')
|
||||||
|
var amount = parseFloat(splitValue[0])
|
||||||
|
var today = new Date()
|
||||||
|
var query
|
||||||
|
if(value.indexOf('min') > -1){
|
||||||
|
query = dateSubtract(today,'minute',amount)
|
||||||
|
}else if(value.indexOf('day') > -1){
|
||||||
|
query = dateSubtract(today,'day',amount)
|
||||||
|
}else if(value.indexOf('hour') > -1){
|
||||||
|
query = dateSubtract(today,'hour',amount)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
knexQuery: knexQuery,
|
knexQuery: knexQuery,
|
||||||
knexQueryPromise: knexQueryPromise,
|
knexQueryPromise: knexQueryPromise,
|
||||||
|
@ -499,5 +529,7 @@ module.exports = function(s,config){
|
||||||
alterColumn,
|
alterColumn,
|
||||||
addColumn,
|
addColumn,
|
||||||
isMySQL,
|
isMySQL,
|
||||||
|
sqlDate,
|
||||||
|
dateSubtract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,12 @@ module.exports = (s,config,lang) => {
|
||||||
async function saveImageFromEvent(options,frameBuffer){
|
async function saveImageFromEvent(options,frameBuffer){
|
||||||
const monitorId = options.mid || options.id
|
const monitorId = options.mid || options.id
|
||||||
const groupKey = options.ke
|
const groupKey = options.ke
|
||||||
if(!frameBuffer || imageSaveEventLock[groupKey + monitorId])return;
|
//if(!frameBuffer || imageSaveEventLock[groupKey + monitorId])return;
|
||||||
|
if(!frameBuffer || frameBuffer.length === 0 || imageSaveEventLock[groupKey + monitorId]) return;
|
||||||
const eventTime = options.time
|
const eventTime = options.time
|
||||||
const objectsFound = options.matrices
|
const objectsFound = options.matrices
|
||||||
const monitorConfig = Object.assign({id: monitorId},s.group[groupKey].rawMonitorConfigurations[monitorId])
|
const monitorConfig = Object.assign({id: monitorId},s.group[groupKey].rawMonitorConfigurations[monitorId])
|
||||||
const timelapseRecordingDirectory = s.getTimelapseFrameDirectory({mid: monitorId, ke: groupKey})
|
const timelapseRecordingDirectory = s.getTimelapseFrameDirectory(monitorConfig)
|
||||||
const currentDate = s.formattedTime(eventTime,'YYYY-MM-DD')
|
const currentDate = s.formattedTime(eventTime,'YYYY-MM-DD')
|
||||||
const filename = s.formattedTime(eventTime) + '.jpg'
|
const filename = s.formattedTime(eventTime) + '.jpg'
|
||||||
const location = timelapseRecordingDirectory + currentDate + '/'
|
const location = timelapseRecordingDirectory + currentDate + '/'
|
||||||
|
@ -79,9 +80,8 @@ module.exports = (s,config,lang) => {
|
||||||
var newString = string + ''
|
var newString = string + ''
|
||||||
var d = Object.assign(eventData,addOps)
|
var d = Object.assign(eventData,addOps)
|
||||||
var detailString = s.stringJSON(d.details)
|
var detailString = s.stringJSON(d.details)
|
||||||
var tag = detailString.matrices
|
var firstMatrix = d.details.matrices ? d.details.matrices[0] : null;
|
||||||
&& detailString.matrices[0]
|
var tag = firstMatrix ? firstMatrix.tag : '';
|
||||||
&& detailString.matrices[0].tag;
|
|
||||||
newString = newString
|
newString = newString
|
||||||
.replace(/{{CONFIDENCE}}/g,d.details.confidence)
|
.replace(/{{CONFIDENCE}}/g,d.details.confidence)
|
||||||
.replace(/{{TIME}}/g,d.currentTimestamp)
|
.replace(/{{TIME}}/g,d.currentTimestamp)
|
||||||
|
@ -91,18 +91,16 @@ module.exports = (s,config,lang) => {
|
||||||
.replace(/{{MONITOR_NAME}}/g,s.group[d.ke].rawMonitorConfigurations[d.id].name)
|
.replace(/{{MONITOR_NAME}}/g,s.group[d.ke].rawMonitorConfigurations[d.id].name)
|
||||||
.replace(/{{GROUP_KEY}}/g,d.ke)
|
.replace(/{{GROUP_KEY}}/g,d.ke)
|
||||||
.replace(/{{DETAILS}}/g,detailString);
|
.replace(/{{DETAILS}}/g,detailString);
|
||||||
if(tag){
|
if(firstMatrix && tag){
|
||||||
newString = newString.replace(/{{TAG}}/g,tag)
|
newString = newString.replace(/{{TAG}}/g,tag)
|
||||||
}
|
}
|
||||||
if(d.details.confidence){
|
if(d.details.confidence || firstMatrix){
|
||||||
newString = newString
|
newString = newString
|
||||||
.replace(/{{CONFIDENCE}}/g,d.details.confidence)
|
.replace(/{{CONFIDENCE}}/g,d.details.confidence || firstMatrix.confidence)
|
||||||
}
|
}
|
||||||
if(newString.includes("REASON")) {
|
if(d.details.reason && newString.includes("REASON")) {
|
||||||
if(d.details.reason) {
|
|
||||||
newString = newString
|
newString = newString
|
||||||
.replace(/{{REASON}}/g, d.details.reason)
|
.replace(/{{REASON}}/g, d.details.reason)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return newString
|
return newString
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,34 @@ module.exports = function(s,config){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.runExtensionsForArray = (nameOfExtension, nameOfExtensionContainer, args) => {
|
||||||
|
nameOfExtensionContainer = nameOfExtensionContainer || `${nameOfExtension}Extensions`
|
||||||
|
const theExtenders = s[nameOfExtensionContainer];
|
||||||
|
for(extender of theExtenders){
|
||||||
|
extender(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.runExtensionsForArrayAwaited = async (nameOfExtension, nameOfExtensionContainer, args) => {
|
||||||
|
nameOfExtensionContainer = nameOfExtensionContainer || `${nameOfExtension}Extensions`
|
||||||
|
const theExtenders = s[nameOfExtensionContainer];
|
||||||
|
for(extender of theExtenders){
|
||||||
|
await extender(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.runExtensionsForObject = (nameOfExtension, nameOfExtensionContainer, args) => {
|
||||||
|
nameOfExtensionContainer = nameOfExtensionContainer || `${nameOfExtension}Extensions`
|
||||||
|
const theExtenders = s[nameOfExtensionContainer];
|
||||||
|
for(extender in theExtenders){
|
||||||
|
extender(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.runExtensionsForObjectAwaited = async (nameOfExtension, nameOfExtensionContainer, args) => {
|
||||||
|
nameOfExtensionContainer = nameOfExtensionContainer || `${nameOfExtension}Extensions`
|
||||||
|
const theExtenders = s[nameOfExtensionContainer];
|
||||||
|
for(extender in theExtenders){
|
||||||
|
await extender(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
////// USER //////
|
////// USER //////
|
||||||
createExtension(`onSocketAuthentication`)
|
createExtension(`onSocketAuthentication`)
|
||||||
createExtension(`onUserLog`)
|
createExtension(`onUserLog`)
|
||||||
|
@ -59,6 +87,11 @@ module.exports = function(s,config){
|
||||||
createExtension(`onSubscriptionCheck`)
|
createExtension(`onSubscriptionCheck`)
|
||||||
createExtension(`onDataPortMessage`)
|
createExtension(`onDataPortMessage`)
|
||||||
createExtension(`onHttpRequestUpgrade`,null,true)
|
createExtension(`onHttpRequestUpgrade`,null,true)
|
||||||
|
/////// CRON ////////
|
||||||
|
createExtension(`onCronGroupProcessed`)
|
||||||
|
createExtension(`onCronGroupProcessedAwaited`)
|
||||||
|
createExtension(`onCronGroupBeforeProcessed`)
|
||||||
|
createExtension(`onCronGroupBeforeProcessedAwaited`)
|
||||||
/////// VIDEOS ////////
|
/////// VIDEOS ////////
|
||||||
createExtension(`insertCompletedVideoExtender`,`insertCompletedVideoExtensions`)
|
createExtension(`insertCompletedVideoExtender`,`insertCompletedVideoExtensions`)
|
||||||
createExtension(`onEventBasedRecordingComplete`)
|
createExtension(`onEventBasedRecordingComplete`)
|
||||||
|
|
|
@ -180,6 +180,9 @@ module.exports = (s,config,lang) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(input.hwaccel_format){
|
||||||
|
inputFlags.push(`-hwaccel_output_format ${input.hwaccel_format}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//custom - input flags
|
//custom - input flags
|
||||||
return `${getInputTypeFlags(input.type)} ${inputFlags.join(' ')} -i "${input.fulladdress}"`
|
return `${getInputTypeFlags(input.type)} ${inputFlags.join(' ')} -i "${input.fulladdress}"`
|
||||||
|
@ -242,7 +245,7 @@ module.exports = (s,config,lang) => {
|
||||||
streamFlags.push(`-c:v ${videoCodec === 'libx264' ? 'h264' : videoCodec}`)
|
streamFlags.push(`-c:v ${videoCodec === 'libx264' ? 'h264' : videoCodec}`)
|
||||||
}
|
}
|
||||||
if(!videoCodecisCopy || outputRequiresEncoding){
|
if(!videoCodecisCopy || outputRequiresEncoding){
|
||||||
if(videoWidth && videoHeight)streamFlags.push(`-s ${videoWidth}x${videoHeight}`)
|
if(videoWidth && videoHeight && !e.details.hwaccel_format) streamFlags.push(`-s ${videoWidth}x${videoHeight}`)
|
||||||
if(videoFps && streamType === 'mjpeg' || streamType === 'b64'){
|
if(videoFps && streamType === 'mjpeg' || streamType === 'b64'){
|
||||||
streamFilters.push(`fps=${videoFps}`)
|
streamFilters.push(`fps=${videoFps}`)
|
||||||
}
|
}
|
||||||
|
@ -362,6 +365,9 @@ module.exports = (s,config,lang) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(e.details.hwaccel_format){
|
||||||
|
inputFlags.push(`-hwaccel_output_format ${e.details.hwaccel_format}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inputFlags.push(`-loglevel ${logLevel}`)
|
inputFlags.push(`-loglevel ${logLevel}`)
|
||||||
//add main input
|
//add main input
|
||||||
|
@ -415,8 +421,13 @@ module.exports = (s,config,lang) => {
|
||||||
streamFlags.push(`-an`)
|
streamFlags.push(`-an`)
|
||||||
}
|
}
|
||||||
if(videoCodec === 'h264_vaapi'){
|
if(videoCodec === 'h264_vaapi'){
|
||||||
streamFilters.push('format=nv12,hwupload');
|
if (!e.details.hwaccel_format) {
|
||||||
|
streamFilters.push('format=nv12,hwupload');
|
||||||
|
}
|
||||||
if(e.details.stream_scale_x && e.details.stream_scale_y){
|
if(e.details.stream_scale_x && e.details.stream_scale_y){
|
||||||
|
if (!e.details.hwaccel_format) {
|
||||||
|
streamFilters.push(',')
|
||||||
|
}
|
||||||
streamFilters.push('scale_vaapi=w='+e.details.stream_scale_x+':h='+e.details.stream_scale_y)
|
streamFilters.push('scale_vaapi=w='+e.details.stream_scale_x+':h='+e.details.stream_scale_y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,7 +437,7 @@ module.exports = (s,config,lang) => {
|
||||||
if(!outputRequiresEncoding && videoCodec !== 'no'){
|
if(!outputRequiresEncoding && videoCodec !== 'no'){
|
||||||
streamFlags.push(`-c:v ` + videoCodec)
|
streamFlags.push(`-c:v ` + videoCodec)
|
||||||
}
|
}
|
||||||
if(!videoCodecisCopy || outputRequiresEncoding){
|
if((!videoCodecisCopy || outputRequiresEncoding) && !e.details.hwaccel_format){
|
||||||
if(videoWidth && videoHeight)streamFlags.push(`-s ${videoWidth}x${videoHeight}`)
|
if(videoWidth && videoHeight)streamFlags.push(`-s ${videoWidth}x${videoHeight}`)
|
||||||
if(videoFps && streamType === 'mjpeg' || streamType === 'b64' || videoFps && !videoCodecisCopy){
|
if(videoFps && streamType === 'mjpeg' || streamType === 'b64' || videoFps && !videoCodecisCopy){
|
||||||
streamFilters.push(`fps=${videoFps}`)
|
streamFilters.push(`fps=${videoFps}`)
|
||||||
|
|
|
@ -27,8 +27,12 @@ module.exports = function(s,config,lang){
|
||||||
getMonitorConfiguration,
|
getMonitorConfiguration,
|
||||||
copyMonitorConfiguration,
|
copyMonitorConfiguration,
|
||||||
checkObjectsInMonitorDetails,
|
checkObjectsInMonitorDetails,
|
||||||
isGroupBelowMaxMonitorCount,
|
|
||||||
} = require('./monitor/utils.js')(s,config,lang)
|
} = require('./monitor/utils.js')(s,config,lang)
|
||||||
|
const {
|
||||||
|
canAddMoreMonitors,
|
||||||
|
sanitizeMonitorConfig,
|
||||||
|
isGroupBelowMaxMonitorCount,
|
||||||
|
} = require('./checker/utils.js')(s,config,lang)
|
||||||
s.initiateMonitorObject = function(e){
|
s.initiateMonitorObject = function(e){
|
||||||
if(!s.group[e.ke]){s.group[e.ke]={}};
|
if(!s.group[e.ke]){s.group[e.ke]={}};
|
||||||
if(!s.group[e.ke].activeMonitors){s.group[e.ke].activeMonitors={}}
|
if(!s.group[e.ke].activeMonitors){s.group[e.ke].activeMonitors={}}
|
||||||
|
@ -196,7 +200,7 @@ module.exports = function(s,config,lang){
|
||||||
var iconImageFile = streamDir + 'icon.jpg'
|
var iconImageFile = streamDir + 'icon.jpg'
|
||||||
const snapRawFilters = monitor.details.cust_snap_raw
|
const snapRawFilters = monitor.details.cust_snap_raw
|
||||||
if(snapRawFilters)outputOptions.push(snapRawFilters);
|
if(snapRawFilters)outputOptions.push(snapRawFilters);
|
||||||
var ffmpegCmd = splitForFFMPEG(`-y -loglevel warning ${isDetectorStream ? '-live_start_index 2' : ''} -re ${inputOptions.join(' ')} -timeout 4000000 -i "${url}" ${outputOptions.join(' ')} -f image2 -an -frames:v 1 "${temporaryImageFile}"`)
|
var ffmpegCmd = splitForFFMPEG(`-y -loglevel warning ${isDetectorStream ? '-live_start_index 2' : ''} -re ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f mjpeg -an -frames:v 1 "${temporaryImageFile}"`)
|
||||||
try{
|
try{
|
||||||
await fs.promises.mkdir(streamDir, {recursive: true}, (err) => {s.debugLog(err)})
|
await fs.promises.mkdir(streamDir, {recursive: true}, (err) => {s.debugLog(err)})
|
||||||
}catch(err){
|
}catch(err){
|
||||||
|
@ -546,10 +550,9 @@ module.exports = function(s,config,lang){
|
||||||
var endData = {
|
var endData = {
|
||||||
ok: false
|
ok: false
|
||||||
}
|
}
|
||||||
if(!form.mid){
|
if(!form.mid || !s.timeReady){
|
||||||
endData.msg = lang['No Monitor ID Present in Form']
|
endData.msg = !s.timeReady ? lang.notReadyYet : lang['No Monitor ID Present in Form']
|
||||||
if(callback)callback(endData);
|
if(callback)callback(endData);
|
||||||
resolve(endData)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
form.mid = form.mid.replace(/[^\w\s]/gi,'').replace(/ /g,'')
|
form.mid = form.mid.replace(/[^\w\s]/gi,'').replace(/ /g,'')
|
||||||
|
@ -563,6 +566,9 @@ module.exports = function(s,config,lang){
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const monitorExists = selectResponse.rows && selectResponse.rows[0];
|
const monitorExists = selectResponse.rows && selectResponse.rows[0];
|
||||||
|
const systemMax = canAddMoreMonitors();
|
||||||
|
const groupMax = isGroupBelowMaxMonitorCount(form.ke);
|
||||||
|
const canDoTheDo = systemMax && groupMax;
|
||||||
var affectMonitor = false
|
var affectMonitor = false
|
||||||
var monitorQuery = {}
|
var monitorQuery = {}
|
||||||
var txData = {
|
var txData = {
|
||||||
|
@ -610,7 +616,7 @@ module.exports = function(s,config,lang){
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
affectMonitor = true
|
affectMonitor = true
|
||||||
}else if(isGroupBelowMaxMonitorCount(form.ke)){
|
}else if(canDoTheDo){
|
||||||
txData.new = true
|
txData.new = true
|
||||||
Object.keys(form).forEach(function(v){
|
Object.keys(form).forEach(function(v){
|
||||||
if(form[v] && form[v] !== ''){
|
if(form[v] && form[v] !== ''){
|
||||||
|
@ -631,7 +637,7 @@ module.exports = function(s,config,lang){
|
||||||
}else{
|
}else{
|
||||||
txData.f = 'monitor_edit_failed'
|
txData.f = 'monitor_edit_failed'
|
||||||
txData.ff = 'max_reached'
|
txData.ff = 'max_reached'
|
||||||
endData.msg = user.lang.monitorEditFailedMaxReached
|
endData.msg = !systemMax ? user.lang.monitorEditFailedMaxReachedUnactivated : user.lang.monitorEditFailedMaxReached
|
||||||
}
|
}
|
||||||
if(affectMonitor === true){
|
if(affectMonitor === true){
|
||||||
form.details = JSON.parse(form.details)
|
form.details = JSON.parse(form.details)
|
||||||
|
@ -650,10 +656,12 @@ module.exports = function(s,config,lang){
|
||||||
}
|
}
|
||||||
s.tx(txData,'GRP_'+form.ke)
|
s.tx(txData,'GRP_'+form.ke)
|
||||||
if(callback)callback(!endData.ok,endData);
|
if(callback)callback(!endData.ok,endData);
|
||||||
let monitorConfig = copyMonitorConfiguration(form.ke,form.mid)
|
if(monitorExists || canDoTheDo){
|
||||||
s.onMonitorSaveExtensions.forEach(function(extender){
|
let monitorConfig = copyMonitorConfiguration(form.ke,form.mid)
|
||||||
extender(monitorConfig,form,endData)
|
s.onMonitorSaveExtensions.forEach(function(extender){
|
||||||
})
|
extender(monitorConfig,form,endData)
|
||||||
|
})
|
||||||
|
}
|
||||||
return endData
|
return endData
|
||||||
}
|
}
|
||||||
s.camera = async (selectedMode,e,cn) => {
|
s.camera = async (selectedMode,e,cn) => {
|
||||||
|
|
|
@ -219,7 +219,7 @@ module.exports = (s,config,lang) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const temporaryImageFile = streamDir + s.gid(5) + '.jpg'
|
const temporaryImageFile = streamDir + s.gid(5) + '.jpg'
|
||||||
const ffmpegCmd = splitForFFMPEG(`-y -loglevel warning -re ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -frames:v 1 "${temporaryImageFile}"`)
|
const ffmpegCmd = splitForFFMPEG(`-y -loglevel warning -re ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f mjpeg -an -frames:v 1 "${temporaryImageFile}"`)
|
||||||
const snapProcess = spawn('ffmpeg',ffmpegCmd,{detached: true})
|
const snapProcess = spawn('ffmpeg',ffmpegCmd,{detached: true})
|
||||||
snapProcess.stderr.on('data',function(data){
|
snapProcess.stderr.on('data',function(data){
|
||||||
// s.debugLog(data.toString())
|
// s.debugLog(data.toString())
|
||||||
|
@ -674,6 +674,8 @@ module.exports = (s,config,lang) => {
|
||||||
mid: monitorId,
|
mid: monitorId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
delete(s.group[groupKey].activeMonitors[monitorId]);
|
||||||
|
delete(s.group[groupKey].rawMonitorConfigurations[monitorId]);
|
||||||
response.msg = `${lang.monitorDeleted} ${lang.byUser} : ${userId}`
|
response.msg = `${lang.monitorDeleted} ${lang.byUser} : ${userId}`
|
||||||
}catch(err){
|
}catch(err){
|
||||||
response.ok = false
|
response.ok = false
|
||||||
|
|
107
libs/socketio.js
107
libs/socketio.js
|
@ -568,55 +568,64 @@ module.exports = function(s,config,lang,io){
|
||||||
if(!d.videoEndDate&&d.endDate){
|
if(!d.videoEndDate&&d.endDate){
|
||||||
d.videoEndDate = stringToSqlTime(d.endDate)
|
d.videoEndDate = stringToSqlTime(d.endDate)
|
||||||
}
|
}
|
||||||
var getVideos = function(callback){
|
var getVideos = function(callback) {
|
||||||
var videoWhereQuery = [
|
var videoWhereQuery = [
|
||||||
['ke','=',cn.ke],
|
['ke','=',cn.ke],
|
||||||
]
|
];
|
||||||
if(d.videoStartDate || d.videoEndDate){
|
|
||||||
if(!d.videoStartDateOperator||d.videoStartDateOperator==''){
|
// Add filtering logic here (startDate, endDate, etc.)
|
||||||
d.videoStartDateOperator='>='
|
if(d.videoStartDate || d.videoEndDate) {
|
||||||
}
|
if(!d.videoStartDateOperator || d.videoStartDateOperator == '') {
|
||||||
if(!d.videoEndDateOperator||d.videoEndDateOperator==''){
|
d.videoStartDateOperator = '>='
|
||||||
d.videoEndDateOperator='<='
|
}
|
||||||
}
|
if(!d.videoEndDateOperator || d.videoEndDateOperator == '') {
|
||||||
switch(true){
|
d.videoEndDateOperator = '<='
|
||||||
case(d.videoStartDate && d.videoStartDate !== '' && d.videoEndDate && d.videoEndDate !== ''):
|
}
|
||||||
videoWhereQuery.push(['time',d.videoStartDateOperator,d.videoStartDate])
|
switch(true) {
|
||||||
videoWhereQuery.push(['end',d.videoEndDateOperator,d.videoEndDate])
|
case(d.videoStartDate && d.videoStartDate !== '' && d.videoEndDate && d.videoEndDate !== ''):
|
||||||
break;
|
videoWhereQuery.push(['time', d.videoStartDateOperator, d.videoStartDate])
|
||||||
case(d.videoStartDate && d.videoStartDate !== ''):
|
videoWhereQuery.push(['end', d.videoEndDateOperator, d.videoEndDate])
|
||||||
videoWhereQuery.push(['time',d.videoStartDateOperator,d.videoStartDate])
|
break;
|
||||||
break;
|
case(d.videoStartDate && d.videoStartDate !== ''):
|
||||||
case(d.videoEndDate && d.videoEndDate !== ''):
|
videoWhereQuery.push(['time', d.videoStartDateOperator, d.videoStartDate])
|
||||||
videoWhereQuery.push(['end',d.videoEndDateOperator,d.videoEndDate])
|
break;
|
||||||
break;
|
case(d.videoEndDate && d.videoEndDate !== ''):
|
||||||
}
|
videoWhereQuery.push(['end', d.videoEndDateOperator, d.videoEndDate])
|
||||||
}
|
break;
|
||||||
if(monitorRestrictions.length > 0){
|
}
|
||||||
videoWhereQuery.push(monitorRestrictions)
|
}
|
||||||
}
|
if(monitorRestrictions.length > 0) {
|
||||||
s.knexQuery({
|
videoWhereQuery.push(monitorRestrictions)
|
||||||
action: "select",
|
}
|
||||||
columns: "*",
|
|
||||||
table: videoSet === 'cloud' ? `Cloud Videos` : "Videos",
|
// Implementing pagination
|
||||||
where: videoWhereQuery,
|
var pageSize = parseInt(d.pageSize) || 10; // Default page size
|
||||||
orderBy: ['time','desc'],
|
var currentPage = parseInt(d.currentPage) || 1; // Default to page 1
|
||||||
limit: d.videoLimit || '100'
|
var offset = (currentPage - 1) * pageSize;
|
||||||
},(err,r) => {
|
|
||||||
if(err){
|
s.knexQuery({
|
||||||
console.error(err)
|
action: "select",
|
||||||
setTimeout(function(){
|
columns: "*",
|
||||||
callback({total:0,limit:d.videoLimit,videos:[]})
|
table: videoSet === 'cloud' ? `Cloud Videos` : "Videos",
|
||||||
},2000)
|
where: videoWhereQuery,
|
||||||
}else{
|
orderBy: ['time','desc'],
|
||||||
s.buildVideoLinks(r,{
|
limit: pageSize, // Limiting the number of rows returned
|
||||||
videoParam : videoSet === 'cloud' ? `cloudVideos` : "videos",
|
offset: offset // Skipping the previous pages' rows
|
||||||
auth : cn.auth
|
},(err,r) => {
|
||||||
})
|
if(err) {
|
||||||
callback({total:r.length,limit:d.videoLimit,videos:r})
|
console.log(err)
|
||||||
}
|
setTimeout(function(){
|
||||||
})
|
callback({total:0, limit:pageSize, videos:[]})
|
||||||
}
|
},2000)
|
||||||
|
} else {
|
||||||
|
s.buildVideoLinks(r,{
|
||||||
|
videoParam: videoSet === 'cloud' ? `cloudVideos` : "videos",
|
||||||
|
auth: cn.auth
|
||||||
|
})
|
||||||
|
callback({total: r.length, limit: pageSize, videos: r})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
getVideos(function(videos){
|
getVideos(function(videos){
|
||||||
getEvents(function(events){
|
getEvents(function(events){
|
||||||
tx({
|
tx({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// https://us-east-1.console.aws.amazon.com/iamv2/home#/users
|
// https://us-east-1.console.aws.amazon.com/iamv2/home#/users
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
|
const { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, StorageClass} = require("@aws-sdk/client-s3");
|
||||||
|
|
||||||
module.exports = function(s,config,lang){
|
module.exports = function(s,config,lang){
|
||||||
const genericRequest = async (groupKey,requestOptions) => {
|
const genericRequest = async (groupKey,requestOptions) => {
|
||||||
|
@ -123,7 +123,8 @@ module.exports = function(s,config,lang){
|
||||||
Bucket: s.group[groupKey].init.aws_s3_bucket,
|
Bucket: s.group[groupKey].init.aws_s3_bucket,
|
||||||
Key: saveLocation,
|
Key: saveLocation,
|
||||||
Body: fileStream,
|
Body: fileStream,
|
||||||
ContentType: 'video/'+e.ext
|
ContentType: 'video/'+e.ext,
|
||||||
|
StorageClass: s.group[groupKey].init.aws_storage_class || StorageClass.STANDARD
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
if(response.err){
|
if(response.err){
|
||||||
s.userLog(e,{type:lang['Amazon S3 Upload Error'],msg:response.err})
|
s.userLog(e,{type:lang['Amazon S3 Upload Error'],msg:response.err})
|
||||||
|
@ -433,6 +434,17 @@ module.exports = function(s,config,lang){
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"name": "detail=aws_storage_class",
|
||||||
|
"field": lang['Storage Class'],
|
||||||
|
"fieldType": "select",
|
||||||
|
"form-group-class": "autosave_aws_s3_input autosave_aws_s3_1",
|
||||||
|
"description": "The storage class of the uploaded objects see https://aws.amazon.com/s3/storage-classes/",
|
||||||
|
"default": StorageClass.STANDARD,
|
||||||
|
"example": StorageClass.STANDARD,
|
||||||
|
"possible": Object.keys(StorageClass).map(k => ({name: k, value: k})),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
"name": "detail=aws_s3_log",
|
"name": "detail=aws_s3_log",
|
||||||
|
|
|
@ -787,14 +787,28 @@ module.exports = function(s,config,lang,app,io){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const cannotSeeImportantSettings = (isRestrictedApiKey && apiKeyPermissions.edit_monitors_disallowed) || userPermissions.monitor_create_disallowed;
|
const cannotSeeImportantSettings = (isRestrictedApiKey && apiKeyPermissions.edit_monitors_disallowed) || userPermissions.monitor_create_disallowed;
|
||||||
|
const whereQuery = [
|
||||||
|
['ke','=',groupKey],
|
||||||
|
monitorRestrictions
|
||||||
|
];
|
||||||
|
if(!!req.query.search){
|
||||||
|
const searchQuery = req.query.search.split(',');
|
||||||
|
const whereQuerySearch = []
|
||||||
|
for(item of searchQuery){
|
||||||
|
if(item){
|
||||||
|
whereQuerySearch.push(
|
||||||
|
whereQuerySearch.length === 0 ? ['name','LIKE',`%${item.trim()}%`] : ['or', 'name','LIKE',`%${item}%`],
|
||||||
|
['or','mid','LIKE',`%${item.trim()}%`]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
whereQuery.push(whereQuerySearch)
|
||||||
|
}
|
||||||
s.knexQuery({
|
s.knexQuery({
|
||||||
action: "select",
|
action: "select",
|
||||||
columns: "*",
|
columns: "*",
|
||||||
table: "Monitors",
|
table: "Monitors",
|
||||||
where: [
|
where: whereQuery
|
||||||
['ke','=',groupKey],
|
|
||||||
monitorRestrictions
|
|
||||||
]
|
|
||||||
},(err,r) => {
|
},(err,r) => {
|
||||||
r.forEach(function(v,n){
|
r.forEach(function(v,n){
|
||||||
const monitorId = v.mid;
|
const monitorId = v.mid;
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
const on = {};
|
||||||
|
const dashboardExtensions = {};
|
||||||
|
async function addExtender(extenderContainer){
|
||||||
|
dashboardExtensions[extenderContainer] = [];
|
||||||
|
on[extenderContainer] = function(...extender){
|
||||||
|
dashboardExtensions[extenderContainer].push(...extender)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function executeExtender(extenderContainer, args){
|
||||||
|
console.log('Running', extenderContainer)
|
||||||
|
for(extender of dashboardExtensions[extenderContainer]){
|
||||||
|
await extender(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var accountSettings = {
|
var accountSettings = {
|
||||||
onLoadFieldsExtensions: [],
|
onLoadFieldsExtensions: [],
|
||||||
onLoadFields: function(...extender){
|
onLoadFields: function(...extender){
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
var liveGridCycleTimer = null;
|
|
||||||
var cycleLiveOptionsBefore = null;
|
|
||||||
var cycleLiveOptions = null;
|
|
||||||
var cycleLiveMoveNext = function(){}
|
|
||||||
var cycleLiveMovePrev = function(){}
|
|
||||||
var cycleLiveFullList = null
|
|
||||||
var cycleLiveCurrentPart = null
|
|
||||||
function getListOfMonitorsToCycleLive(chosenTags,useMonitorIds){
|
|
||||||
var monitors = []
|
|
||||||
if(useMonitorIds){
|
|
||||||
monitors = getMonitorsFromIds(chosenTags)
|
|
||||||
}else if(chosenTags){
|
|
||||||
var tags = sanitizeTagList(chosenTags)
|
|
||||||
monitors = getMonitorsFromTags(tags)
|
|
||||||
}else{
|
|
||||||
monitors = getRunningMonitors(true)
|
|
||||||
}
|
|
||||||
return monitors;
|
|
||||||
}
|
|
||||||
function getPartForCycleLive(fullList, afterMonitorId, numberOfMonitors) {
|
|
||||||
const startIndex = afterMonitorId ? fullList.findIndex(monitor => monitor.mid === afterMonitorId) : -1;
|
|
||||||
const result = [];
|
|
||||||
for (let i = 1; i <= numberOfMonitors; i++) {
|
|
||||||
const index = (startIndex + i) % fullList.length;
|
|
||||||
result.push(fullList[index]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
function displayCycleSetOnLiveGrid(monitorsList){
|
|
||||||
cycleLiveCurrentPart = [].concat(monitorsList)
|
|
||||||
closeAllLiveGridPlayers()
|
|
||||||
monitorsWatchOnLiveGrid(monitorsList.map(monitor => monitor.mid))
|
|
||||||
}
|
|
||||||
// rotator
|
|
||||||
function stopCycleLive(){
|
|
||||||
clearTimeout(liveGridCycleTimer)
|
|
||||||
liveGridCycleTimer = null
|
|
||||||
}
|
|
||||||
function resumeCycleLive(fullList,partForCycle,numberOfMonitors){
|
|
||||||
const theLocalStorage = dashboardOptions()
|
|
||||||
const cycleLiveTimerAmount = parseInt(theLocalStorage.cycleLiveTimerAmount) || 30000
|
|
||||||
function next(){
|
|
||||||
var afterMonitorId = partForCycle.slice(-1)[0].mid;
|
|
||||||
partForCycle = getPartForCycleLive(fullList,afterMonitorId,numberOfMonitors)
|
|
||||||
displayCycleSetOnLiveGrid(partForCycle)
|
|
||||||
reset()
|
|
||||||
}
|
|
||||||
function prev(){
|
|
||||||
var firstInPart = partForCycle[0].mid;
|
|
||||||
var firstPartIndex = fullList.findIndex(monitor => monitor.mid === firstInPart)
|
|
||||||
var backedToIndex = (firstPartIndex - (numberOfMonitors + 1) + fullList.length) % fullList.length;
|
|
||||||
var beforeMonitorId = fullList[backedToIndex].mid
|
|
||||||
partForCycle = getPartForCycleLive(fullList,beforeMonitorId,numberOfMonitors, true)
|
|
||||||
displayCycleSetOnLiveGrid(partForCycle)
|
|
||||||
reset()
|
|
||||||
}
|
|
||||||
function reset(){
|
|
||||||
clearTimeout(liveGridCycleTimer)
|
|
||||||
liveGridCycleTimer = setTimeout(function(){
|
|
||||||
next()
|
|
||||||
},cycleLiveTimerAmount)
|
|
||||||
}
|
|
||||||
reset()
|
|
||||||
cycleLiveMoveNext = next
|
|
||||||
cycleLiveMovePrev = prev
|
|
||||||
}
|
|
||||||
function beginCycleLive({
|
|
||||||
chosenTags,
|
|
||||||
useMonitorIds,
|
|
||||||
numberOfMonitors,
|
|
||||||
monitorHeight,
|
|
||||||
}){
|
|
||||||
var fullList = getListOfMonitorsToCycleLive(chosenTags,useMonitorIds)
|
|
||||||
var partForCycle = getPartForCycleLive(fullList,null,numberOfMonitors)
|
|
||||||
cycleLiveFullList = [].concat(fullList)
|
|
||||||
displayCycleSetOnLiveGrid(partForCycle)
|
|
||||||
stopCycleLive()
|
|
||||||
resumeCycleLive(fullList,partForCycle,numberOfMonitors)
|
|
||||||
}
|
|
||||||
dashboardSwitchCallbacks.cycleLiveGrid = function(toggleState){
|
|
||||||
if(toggleState !== 1){
|
|
||||||
cycleLiveOptions = null
|
|
||||||
cycleLiveOptionsBefore = null
|
|
||||||
stopCycleLive()
|
|
||||||
}else{
|
|
||||||
openTab('liveGrid')
|
|
||||||
cycleLiveOptionsBefore = cycleLiveOptions ? Object.assign({},cycleLiveOptions) : null
|
|
||||||
const theLocalStorage = dashboardOptions()
|
|
||||||
const cycleLivePerRow = parseInt(theLocalStorage.cycleLivePerRow) || 2
|
|
||||||
const cycleLiveNumberOfMonitors = parseInt(theLocalStorage.cycleLiveNumberOfMonitors) || 4
|
|
||||||
const cycleLiveMonitorHeight = parseInt(theLocalStorage.cycleLiveMonitorHeight) || 4
|
|
||||||
cycleLiveOptions = {
|
|
||||||
chosenTags: null,
|
|
||||||
useMonitorIds: null,
|
|
||||||
monitorsPerRow: cycleLivePerRow,
|
|
||||||
numberOfMonitors: cycleLiveNumberOfMonitors,
|
|
||||||
monitorHeight: cycleLiveMonitorHeight,
|
|
||||||
}
|
|
||||||
beginCycleLive(cycleLiveOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function keyShortcutsForCycleLive(enable) {
|
|
||||||
function cleanup(){
|
|
||||||
document.removeEventListener('keydown', keyShortcuts['cycleLive'].keydown);
|
|
||||||
document.removeEventListener('keyup', keyShortcuts['cycleLive'].keyup);
|
|
||||||
delete(keyShortcuts['cycleLive'])
|
|
||||||
}
|
|
||||||
if(enable){
|
|
||||||
let isKeyPressed = false;
|
|
||||||
function handleKeyboard(event){
|
|
||||||
if (isKeyPressed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
switch(event.code){
|
|
||||||
case 'Space':
|
|
||||||
isKeyPressed = true;
|
|
||||||
if(liveGridCycleTimer){
|
|
||||||
stopCycleLive()
|
|
||||||
}else{
|
|
||||||
resumeCycleLive(
|
|
||||||
cycleLiveFullList,
|
|
||||||
cycleLiveCurrentPart,
|
|
||||||
cycleLiveOptions.numberOfMonitors
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'ArrowLeft':
|
|
||||||
isKeyPressed = true;
|
|
||||||
cycleLiveMovePrev();
|
|
||||||
break;
|
|
||||||
case 'ArrowRight':
|
|
||||||
isKeyPressed = true;
|
|
||||||
cycleLiveMoveNext();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handleKeyup(event) {
|
|
||||||
isKeyPressed = false;
|
|
||||||
}
|
|
||||||
keyShortcuts['cycleLive'] = {
|
|
||||||
keydown: handleKeyboard,
|
|
||||||
keyup: handleKeyup,
|
|
||||||
}
|
|
||||||
document.addEventListener('keydown', keyShortcuts['cycleLive'].keydown);
|
|
||||||
document.addEventListener('keyup', keyShortcuts['cycleLive'].keyup);
|
|
||||||
}else{
|
|
||||||
cleanup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addOnTabOpen('liveGrid', function () {
|
|
||||||
keyShortcutsForCycleLive(true)
|
|
||||||
})
|
|
||||||
addOnTabReopen('liveGrid', function () {
|
|
||||||
if(cycleLiveOptions){
|
|
||||||
beginCycleLive(cycleLiveOptions)
|
|
||||||
}
|
|
||||||
keyShortcutsForCycleLive(true)
|
|
||||||
})
|
|
||||||
addOnTabAway('liveGrid', function () {
|
|
||||||
stopCycleLive()
|
|
||||||
keyShortcutsForCycleLive(false)
|
|
||||||
})
|
|
|
@ -656,6 +656,11 @@ function closeLiveGridPlayer(monitorId,killElement){
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function closeLiveGridPlayers(monitors, killElement){
|
||||||
|
$.each(monitors,function(n,v){
|
||||||
|
monitorWatchOnLiveGrid(v.mid, killElement)
|
||||||
|
})
|
||||||
|
}
|
||||||
function monitorWatchOnLiveGrid(monitorId, watchOff){
|
function monitorWatchOnLiveGrid(monitorId, watchOff){
|
||||||
return mainSocket.f({f:'monitor',ff:watchOff ? 'watch_off' : 'watch_on',id: monitorId})
|
return mainSocket.f({f:'monitor',ff:watchOff ? 'watch_off' : 'watch_on',id: monitorId})
|
||||||
}
|
}
|
||||||
|
@ -664,13 +669,20 @@ function monitorsWatchOnLiveGrid(monitorIds, watchOff){
|
||||||
monitorWatchOnLiveGrid(monitorId, watchOff)
|
monitorWatchOnLiveGrid(monitorId, watchOff)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function callMonitorToLiveGrid(v){
|
function callMonitorToLiveGrid(v, justTry){
|
||||||
var watchedOn = dashboardOptions().watch_on || {}
|
var watchedOn = dashboardOptions().watch_on || {}
|
||||||
if(watchedOn[v.ke] && watchedOn[v.ke][v.mid] === 1 && loadedMonitors[v.mid] && loadedMonitors[v.mid].mode !== 'stop'){
|
if(justTry || watchedOn[v.ke] && watchedOn[v.ke][v.mid] === 1 && loadedMonitors[v.mid] && loadedMonitors[v.mid].mode !== 'stop'){
|
||||||
mainSocket.f({f:'monitor',ff:'watch_on',id:v.mid})
|
mainSocket.f({f:'monitor',ff:'watch_on',id:v.mid})
|
||||||
if(tabTree.name !== 'monitorSettings')openLiveGrid()
|
if(tabTree.name !== 'monitorSettings')openLiveGrid()
|
||||||
|
console.log('loaded',v.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function callMonitorsToLiveGrid(monitors, justTry){
|
||||||
|
$.each(monitors,function(n,v){
|
||||||
|
console.log('loading',v.name)
|
||||||
|
callMonitorToLiveGrid(v, justTry)
|
||||||
|
})
|
||||||
|
}
|
||||||
function loadPreviouslyOpenedLiveGridBlocks(){
|
function loadPreviouslyOpenedLiveGridBlocks(){
|
||||||
$.getJSON(getApiPrefix(`monitor`),function(data){
|
$.getJSON(getApiPrefix(`monitor`),function(data){
|
||||||
$.each(data,function(n,v){
|
$.each(data,function(n,v){
|
||||||
|
@ -1279,8 +1291,8 @@ $(document).ready(function(e){
|
||||||
var monitorId = d.mid || d.id
|
var monitorId = d.mid || d.id
|
||||||
var loadedMonitor = loadedMonitors[monitorId]
|
var loadedMonitor = loadedMonitors[monitorId]
|
||||||
var subStreamChannel = d.subStreamChannel
|
var subStreamChannel = d.subStreamChannel
|
||||||
var monitorsPerRow = cycleLiveOptions ? cycleLiveOptions.monitorsPerRow : null;
|
var monitorsPerRow = null;
|
||||||
var monitorHeight = cycleLiveOptions ? cycleLiveOptions.monitorHeight : null;
|
var monitorHeight = null;
|
||||||
if(!loadedMonitor.subStreamChannel && loadedMonitor.details.stream_type === 'useSubstream'){
|
if(!loadedMonitor.subStreamChannel && loadedMonitor.details.stream_type === 'useSubstream'){
|
||||||
toggleSubStream(monitorId,function(){
|
toggleSubStream(monitorId,function(){
|
||||||
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel,monitorsPerRow,monitorHeight)
|
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel,monitorsPerRow,monitorHeight)
|
||||||
|
@ -1428,4 +1440,9 @@ $(document).ready(function(e){
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
dashboardSwitchCallbacks.jpegMode = toggleJpegMode
|
dashboardSwitchCallbacks.jpegMode = toggleJpegMode
|
||||||
|
window.openLiveGrid = openLiveGrid;
|
||||||
|
window.callMonitorToLiveGrid = callMonitorToLiveGrid;
|
||||||
|
window.monitorsWatchOnLiveGrid = monitorsWatchOnLiveGrid;
|
||||||
|
window.closeAllLiveGridPlayers = closeAllLiveGridPlayers;
|
||||||
|
window.closeLiveGridPlayers = closeLiveGridPlayers;
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
function keyShortcutsForLiveGridUtils(enable) {
|
// function keyShortcutsForLiveGridUtils(enable) {
|
||||||
function cleanup(){
|
// function cleanup(){
|
||||||
document.removeEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
// document.removeEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||||
document.removeEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
// document.removeEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||||
delete(keyShortcuts['liveGridUtils'])
|
// delete(keyShortcuts['liveGridUtils'])
|
||||||
}
|
// }
|
||||||
if(enable){
|
// if(enable){
|
||||||
let isKeyPressed = false;
|
// let isKeyPressed = false;
|
||||||
function handleKeyboard(event){
|
// function handleKeyboard(event){
|
||||||
if (isKeyPressed) {
|
// if (isKeyPressed) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
event.preventDefault();
|
// event.preventDefault();
|
||||||
switch(event.code){
|
// switch(event.code){
|
||||||
case 'Enter':
|
// case 'Enter':
|
||||||
addMarkAsEventToAllOpenMonitors()
|
// addMarkAsEventToAllOpenMonitors()
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
function handleKeyup(event) {
|
// function handleKeyup(event) {
|
||||||
isKeyPressed = false;
|
// isKeyPressed = false;
|
||||||
}
|
// }
|
||||||
keyShortcuts['liveGridUtils'] = {
|
// keyShortcuts['liveGridUtils'] = {
|
||||||
keydown: handleKeyboard,
|
// keydown: handleKeyboard,
|
||||||
keyup: handleKeyup,
|
// keyup: handleKeyup,
|
||||||
}
|
// }
|
||||||
document.addEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
// document.addEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||||
document.addEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
// document.addEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||||
}else{
|
// }else{
|
||||||
cleanup()
|
// cleanup()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
addOnTabOpen('liveGrid', function () {
|
// addOnTabOpen('liveGrid', function () {
|
||||||
keyShortcutsForLiveGridUtils(true)
|
// keyShortcutsForLiveGridUtils(true)
|
||||||
})
|
// })
|
||||||
addOnTabReopen('liveGrid', function () {
|
// addOnTabReopen('liveGrid', function () {
|
||||||
keyShortcutsForLiveGridUtils(true)
|
// keyShortcutsForLiveGridUtils(true)
|
||||||
})
|
// })
|
||||||
addOnTabAway('liveGrid', function () {
|
// addOnTabAway('liveGrid', function () {
|
||||||
keyShortcutsForLiveGridUtils(false)
|
// keyShortcutsForLiveGridUtils(false)
|
||||||
})
|
// })
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
var loadedMap;
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
var theBlock = $('#tab-monitorMap')
|
var theBlock = $('#tab-monitorMap')
|
||||||
var theMap = $('#monitor-map-canvas')
|
var theMap = $('#monitor-map-canvas')
|
||||||
var loadedMap;
|
|
||||||
function loadPopupVideoList(monitor){
|
function loadPopupVideoList(monitor){
|
||||||
var groupKey = monitor.ke
|
var groupKey = monitor.ke
|
||||||
var monitorId = monitor.mid
|
var monitorId = monitor.mid
|
||||||
|
|
|
@ -62,6 +62,7 @@ function generateDefaultMonitorSettings(){
|
||||||
"hwaccel": "auto",
|
"hwaccel": "auto",
|
||||||
"hwaccel_vcodec": "",
|
"hwaccel_vcodec": "",
|
||||||
"hwaccel_device": "",
|
"hwaccel_device": "",
|
||||||
|
"hwaccel_format": "",
|
||||||
"use_coprocessor": null,
|
"use_coprocessor": null,
|
||||||
"stream_type": "hls",
|
"stream_type": "hls",
|
||||||
"stream_flv_type": "http",
|
"stream_flv_type": "http",
|
||||||
|
@ -274,6 +275,7 @@ function generateDefaultMonitorSettings(){
|
||||||
"hwaccel": null,
|
"hwaccel": null,
|
||||||
"hwaccel_vcodec": "",
|
"hwaccel_vcodec": "",
|
||||||
"hwaccel_device": "",
|
"hwaccel_device": "",
|
||||||
|
"hwaccel_format": "",
|
||||||
"cust_input": ""
|
"cust_input": ""
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
|
@ -412,6 +414,7 @@ var copyMonitorSettingsToSelected = function(monitorConfig){
|
||||||
monitor.details.hwaccel = monitorDetails.hwaccel
|
monitor.details.hwaccel = monitorDetails.hwaccel
|
||||||
monitor.details.hwaccel_vcodec = monitorDetails.hwaccel_vcodec
|
monitor.details.hwaccel_vcodec = monitorDetails.hwaccel_vcodec
|
||||||
monitor.details.hwaccel_device = monitorDetails.hwaccel_device
|
monitor.details.hwaccel_device = monitorDetails.hwaccel_device
|
||||||
|
monitor.details.hwaccel_format = monitorDetails.hwaccel_format
|
||||||
}else{
|
}else{
|
||||||
monitor = Object.assign({},loadedMonitors[id]);
|
monitor = Object.assign({},loadedMonitors[id]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ $(document).ready(function(e){
|
||||||
var monitorSettingsMapOptionsEl = $('#monitor-settings-geolocation-options')
|
var monitorSettingsMapOptionsEl = $('#monitor-settings-geolocation-options')
|
||||||
var monitorSettingsMapOptionsElOptions = monitorSettingsMapOptionsEl.find('[map-option]')
|
var monitorSettingsMapOptionsElOptions = monitorSettingsMapOptionsEl.find('[map-option]')
|
||||||
var editorForm = monitorEditorWindow.find('form')
|
var editorForm = monitorEditorWindow.find('form')
|
||||||
var loadedMap;
|
var mapInWindow;
|
||||||
var monitorMapMarker;
|
var monitorMapMarker;
|
||||||
var monitorMapMarkerFov;
|
var monitorMapMarkerFov;
|
||||||
function setAdditionalControls(options){
|
function setAdditionalControls(options){
|
||||||
|
@ -34,14 +34,14 @@ $(document).ready(function(e){
|
||||||
fov,
|
fov,
|
||||||
range,
|
range,
|
||||||
} = getGeolocationParts(geoString || monitor.details.geolocation);
|
} = getGeolocationParts(geoString || monitor.details.geolocation);
|
||||||
loadedMap = L.map('monitor-settings-monitor-map').setView([lat, lng], zoom);
|
mapInWindow = L.map('monitor-settings-monitor-map').setView([lat, lng], zoom);
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
}).addTo(loadedMap);
|
}).addTo(mapInWindow);
|
||||||
monitorMapMarker = L.marker([lat, lng], {
|
monitorMapMarker = L.marker([lat, lng], {
|
||||||
title: monitor ? `${monitor.name} (${monitor.host})` : null,
|
title: monitor ? `${monitor.name} (${monitor.host})` : null,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
}).addTo(loadedMap);
|
}).addTo(mapInWindow);
|
||||||
monitorMapMarker.on('dragend', function(){
|
monitorMapMarker.on('dragend', function(){
|
||||||
setGeolocationFieldValue()
|
setGeolocationFieldValue()
|
||||||
});
|
});
|
||||||
|
@ -49,7 +49,7 @@ $(document).ready(function(e){
|
||||||
var markerDetails = getMapMarkerDetails();
|
var markerDetails = getMapMarkerDetails();
|
||||||
setMapMarkerFov(monitorMapMarkerFov,markerDetails)
|
setMapMarkerFov(monitorMapMarkerFov,markerDetails)
|
||||||
});
|
});
|
||||||
loadedMap.on('zoomend', function(){
|
mapInWindow.on('zoomend', function(){
|
||||||
setGeolocationFieldValue()
|
setGeolocationFieldValue()
|
||||||
});
|
});
|
||||||
setAdditionalControls({
|
setAdditionalControls({
|
||||||
|
@ -57,7 +57,7 @@ $(document).ready(function(e){
|
||||||
fov,
|
fov,
|
||||||
range,
|
range,
|
||||||
})
|
})
|
||||||
monitorMapMarkerFov = drawMapMarkerFov(loadedMap,{
|
monitorMapMarkerFov = drawMapMarkerFov(mapInWindow,{
|
||||||
lat,
|
lat,
|
||||||
lng,
|
lng,
|
||||||
direction,
|
direction,
|
||||||
|
@ -71,8 +71,8 @@ $(document).ready(function(e){
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function unloadMap(){
|
function unloadMap(){
|
||||||
loadedMap.remove();
|
mapInWindow.remove();
|
||||||
loadedMap = null;
|
mapInWindow = null;
|
||||||
}
|
}
|
||||||
function getMapOptions(){
|
function getMapOptions(){
|
||||||
var options = {}
|
var options = {}
|
||||||
|
@ -86,7 +86,7 @@ $(document).ready(function(e){
|
||||||
}
|
}
|
||||||
function getMapMarkerDetails(){
|
function getMapMarkerDetails(){
|
||||||
var pos = monitorMapMarker.getLatLng()
|
var pos = monitorMapMarker.getLatLng()
|
||||||
var zoom = loadedMap.getZoom();
|
var zoom = mapInWindow.getZoom();
|
||||||
var {
|
var {
|
||||||
direction,
|
direction,
|
||||||
fov,
|
fov,
|
||||||
|
|
|
@ -110,7 +110,7 @@ $(document).ready(function(){
|
||||||
async function loopOnGaps(monitorId){
|
async function loopOnGaps(monitorId){
|
||||||
for (let i = 0; i < gaps.length; i++) {
|
for (let i = 0; i < gaps.length; i++) {
|
||||||
var range = gaps[i]
|
var range = gaps[i]
|
||||||
videos.push(...(await getVideos({
|
var videosFound = (await getVideos({
|
||||||
monitorId,
|
monitorId,
|
||||||
startDate: range[0],
|
startDate: range[0],
|
||||||
endDate: range[1],
|
endDate: range[1],
|
||||||
|
@ -118,7 +118,9 @@ $(document).ready(function(){
|
||||||
searchQuery,
|
searchQuery,
|
||||||
// archived: false,
|
// archived: false,
|
||||||
// customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
// customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
||||||
},null,dontShowDetectionOnTimeline)).videos);
|
},null,dontShowDetectionOnTimeline)).videos;
|
||||||
|
videos.push(...videosFound);
|
||||||
|
executeExtender('timelineGetVideosByMonitor', [monitorId, videosFound])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(monitorIds && monitorIds.length > 0){
|
if(monitorIds && monitorIds.length > 0){
|
||||||
|
@ -148,6 +150,7 @@ $(document).ready(function(){
|
||||||
setLoadingMask(true)
|
setLoadingMask(true)
|
||||||
timeStripListOfQueries.push(...gaps)
|
timeStripListOfQueries.push(...gaps)
|
||||||
var videos = await getVideosInGaps(gaps,timeStripSelectedMonitors)
|
var videos = await getVideosInGaps(gaps,timeStripSelectedMonitors)
|
||||||
|
executeExtender('timelineGetVideos', [videos])
|
||||||
videos = addVideoBeforeAndAfter(videos)
|
videos = addVideoBeforeAndAfter(videos)
|
||||||
loadedVideosOnTimeStrip.push(...videos)
|
loadedVideosOnTimeStrip.push(...videos)
|
||||||
if(currentVideosLength !== loadedVideosOnTimeStrip.length)addTimelineItems(loadedVideosOnTimeStrip);
|
if(currentVideosLength !== loadedVideosOnTimeStrip.length)addTimelineItems(loadedVideosOnTimeStrip);
|
||||||
|
@ -293,6 +296,7 @@ $(document).ready(function(){
|
||||||
timeStripActionWithPausePlay().then((timeChanging) => {
|
timeStripActionWithPausePlay().then((timeChanging) => {
|
||||||
if(!timeChanging){
|
if(!timeChanging){
|
||||||
resetTimeline(clickTime)
|
resetTimeline(clickTime)
|
||||||
|
executeExtender('timelineTimeChange', [clickTime])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -313,6 +317,7 @@ $(document).ready(function(){
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
timeChanging = false
|
timeChanging = false
|
||||||
getAndDrawVideosToTimeline(clickTime)
|
getAndDrawVideosToTimeline(clickTime)
|
||||||
|
executeExtender('timelineRangeChanged', [properties.start, properties.end, getTimestripDate()])
|
||||||
},500)
|
},500)
|
||||||
},300)
|
},300)
|
||||||
})
|
})
|
||||||
|
@ -641,6 +646,7 @@ $(document).ready(function(){
|
||||||
addition += (msSpeed * timelineSpeed);
|
addition += (msSpeed * timelineSpeed);
|
||||||
newTime = new Date(currentDate + addition)
|
newTime = new Date(currentDate + addition)
|
||||||
setTickDate(newTime);
|
setTickDate(newTime);
|
||||||
|
executeExtender('timelineTimeChange', [newTime])
|
||||||
// setTimeOfCanvasVideos(newTime)
|
// setTimeOfCanvasVideos(newTime)
|
||||||
}, msSpeed)
|
}, msSpeed)
|
||||||
timeStripVisTickMovementIntervalSecond = setInterval(function() {
|
timeStripVisTickMovementIntervalSecond = setInterval(function() {
|
||||||
|
@ -911,9 +917,25 @@ $(document).ready(function(){
|
||||||
onSelectedMonitorChange()
|
onSelectedMonitorChange()
|
||||||
refreshTimeline()
|
refreshTimeline()
|
||||||
}
|
}
|
||||||
|
window.resetTimelineWithMonitors = function(monitorIds, start = new Date(), end = new Date(), tickTime){
|
||||||
|
setTimeout(() => {
|
||||||
|
timeStripSelectedMonitors = monitorIds || [];
|
||||||
|
onSelectedMonitorChange()
|
||||||
|
setLoadingMask(true)
|
||||||
|
dateRangeChanging = true
|
||||||
|
setTimestripDate(start, end)
|
||||||
|
setTimeout(() => {
|
||||||
|
dateRangeChanging = false
|
||||||
|
refreshTimeline()
|
||||||
|
var newTickPosition = tickTime || getTimeBetween(start,end,50);
|
||||||
|
setTickDate(newTickPosition)
|
||||||
|
},2000)
|
||||||
|
},1000)
|
||||||
|
openTab('timeline')
|
||||||
|
}
|
||||||
function refreshTimelineOnAgree(){
|
function refreshTimelineOnAgree(){
|
||||||
var askToLoad = isAllMonitorsSelected(50)
|
var askToLoad = isAllMonitorsSelected(50)
|
||||||
if(askToLoad){
|
if(!window.skipTimelineAgree && askToLoad){
|
||||||
$.confirm.create({
|
$.confirm.create({
|
||||||
title: lang.tooManyMonitorsSelected,
|
title: lang.tooManyMonitorsSelected,
|
||||||
body: lang.performanceMayBeAffected,
|
body: lang.performanceMayBeAffected,
|
||||||
|
@ -931,6 +953,7 @@ $(document).ready(function(){
|
||||||
}else{
|
}else{
|
||||||
refreshTimeline()
|
refreshTimeline()
|
||||||
}
|
}
|
||||||
|
window.skipTimelineAgree = false;
|
||||||
}
|
}
|
||||||
function monitorSelectorController(){
|
function monitorSelectorController(){
|
||||||
var el = $(this)
|
var el = $(this)
|
||||||
|
@ -1072,4 +1095,8 @@ $(document).ready(function(){
|
||||||
if(currentOptions.dontShowDetectionOnTimeline === '1'){
|
if(currentOptions.dontShowDetectionOnTimeline === '1'){
|
||||||
timeStripDontShowDetectionToggle()
|
timeStripDontShowDetectionToggle()
|
||||||
}
|
}
|
||||||
|
addExtender('timelineTimeChange')
|
||||||
|
addExtender('timelineRangeChanged')
|
||||||
|
addExtender('timelineGetVideos')
|
||||||
|
addExtender('timelineGetVideosByMonitor')
|
||||||
})
|
})
|
||||||
|
|
|
@ -79,35 +79,69 @@ function createVideoLinks(video,options){
|
||||||
video.details = details
|
video.details = details
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
function applyDataListToVideos(videos,events,keyName,reverseList){
|
function applyDataListToVideos(videos, events, keyName, reverseList) {
|
||||||
var updatedVideos = videos.concat([])
|
const eventMap = new Map();
|
||||||
var currentEvents = events.concat([])
|
|
||||||
updatedVideos.forEach(function(video){
|
// Build a map of events by monitor ID
|
||||||
var videoEvents = []
|
events.forEach(event => {
|
||||||
currentEvents.forEach(function(theEvent,index){
|
if (!eventMap.has(event.mid)) {
|
||||||
var startTime = new Date(video.time)
|
eventMap.set(event.mid, []);
|
||||||
var endTime = new Date(video.end)
|
}
|
||||||
var eventTime = new Date(theEvent.time)
|
eventMap.get(event.mid).push(event);
|
||||||
if(theEvent.mid === video.mid && eventTime >= startTime && eventTime <= endTime){
|
});
|
||||||
videoEvents.push(theEvent)
|
|
||||||
currentEvents.splice(index, 1)
|
// Attach events to videos
|
||||||
}
|
videos.forEach(video => {
|
||||||
})
|
const videoEvents = eventMap.get(video.mid) || [];
|
||||||
if(reverseList)videoEvents = videoEvents.reverse()
|
const matchedEvents = videoEvents.filter(event => {
|
||||||
video[keyName || 'events'] = videoEvents
|
const startTime = new Date(video.time);
|
||||||
})
|
const endTime = new Date(video.end);
|
||||||
return updatedVideos
|
const eventTime = new Date(event.time);
|
||||||
|
return eventTime >= startTime && eventTime <= endTime;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reverseList) matchedEvents.reverse();
|
||||||
|
|
||||||
|
video[keyName || 'events'] = matchedEvents;
|
||||||
|
});
|
||||||
|
|
||||||
|
return videos;
|
||||||
}
|
}
|
||||||
function applyTimelapseFramesListToVideos(videos,events,keyName,reverseList){
|
function applyTimelapseFramesListToVideos(videos, events, keyName, reverseList) {
|
||||||
var thisApiPrefix = getApiPrefix() + '/timelapse/' + $user.ke + '/'
|
const thisApiPrefix = `${getApiPrefix()}/timelapse/${$user.ke}/`;
|
||||||
var newVideos = applyDataListToVideos(videos,events,keyName,reverseList)
|
const eventMap = new Map();
|
||||||
newVideos.forEach(function(video){
|
|
||||||
video.timelapseFrames.forEach(function(row){
|
// Build a map of events by monitor ID
|
||||||
var apiURL = thisApiPrefix + row.mid
|
events.forEach(event => {
|
||||||
row.href = libURL + apiURL + '/' + row.filename.split('T')[0] + '/' + row.filename
|
if (!eventMap.has(event.mid)) {
|
||||||
})
|
eventMap.set(event.mid, []);
|
||||||
})
|
}
|
||||||
return newVideos
|
eventMap.get(event.mid).push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attach timelapse frames to videos
|
||||||
|
videos.forEach(video => {
|
||||||
|
const videoEvents = eventMap.get(video.mid) || [];
|
||||||
|
const matchedEvents = videoEvents.filter(event => {
|
||||||
|
const startTime = new Date(video.time);
|
||||||
|
const endTime = new Date(video.end);
|
||||||
|
const eventTime = new Date(event.time);
|
||||||
|
return eventTime >= startTime && eventTime <= endTime;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reverseList) matchedEvents.reverse();
|
||||||
|
|
||||||
|
// Assigning matched events to video
|
||||||
|
video[keyName || 'timelapseFrames'] = matchedEvents.map(row => {
|
||||||
|
const apiURL = `${thisApiPrefix}${row.mid}`;
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
href: `${libURL}${apiURL}/${row.filename.split('T')[0]}/${row.filename}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return videos;
|
||||||
}
|
}
|
||||||
function getFrameOnVideoRow(percentageInward, video) {
|
function getFrameOnVideoRow(percentageInward, video) {
|
||||||
var startTime = video.time;
|
var startTime = video.time;
|
||||||
|
|
|
@ -26,16 +26,20 @@ $(document).ready(function(e){
|
||||||
return href
|
return href
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function loadFramesForVideosInView(){
|
//Lazy Load Thumbnails
|
||||||
videosTableDrawArea.find('.video-thumbnail').each(async (n,imgEl) => {
|
function loadFramesForVideosInView() {
|
||||||
const el = $(imgEl)
|
videosTableDrawArea.find('.video-thumbnail').each(async (n, imgEl) => {
|
||||||
const monitorId = el.attr('data-mid')
|
const el = $(imgEl);
|
||||||
const startDate = el.attr('data-time')
|
const monitorId = el.attr('data-mid');
|
||||||
const endDate = el.attr('data-end')
|
const startDate = el.attr('data-time');
|
||||||
const imgBlock = el.find('.video-thumbnail-img-block')
|
const endDate = el.attr('data-end');
|
||||||
const href = await getSnapshotFromVideoTimeFrame(monitorId,startDate,endDate)
|
const imgBlock = el.find('.video-thumbnail-img-block');
|
||||||
imgBlock.find('img').attr('src',href)
|
|
||||||
})
|
if (el.is(':visible')) { // Only load if visible
|
||||||
|
const href = await getSnapshotFromVideoTimeFrame(monitorId, startDate, endDate);
|
||||||
|
imgBlock.find('img').attr('src', href);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
window.openVideosTableView = function(monitorId){
|
window.openVideosTableView = function(monitorId){
|
||||||
drawMonitorListToSelector(monitorsList,null,null,true)
|
drawMonitorListToSelector(monitorsList,null,null,true)
|
||||||
|
@ -45,143 +49,169 @@ $(document).ready(function(e){
|
||||||
}
|
}
|
||||||
loadDateRangePicker(dateSelector,{
|
loadDateRangePicker(dateSelector,{
|
||||||
onChange: function(start, end, label) {
|
onChange: function(start, end, label) {
|
||||||
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
drawVideosTableViewElements()
|
drawVideosTableViewElements()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
monitorsList.change(function(){
|
function debounce(func, wait) {
|
||||||
drawVideosTableViewElements()
|
let timeout;
|
||||||
})
|
return function(...args) {
|
||||||
objectTagSearchField.change(function(){
|
clearTimeout(timeout);
|
||||||
drawVideosTableViewElements()
|
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||||
})
|
};
|
||||||
cloudVideoCheckSwitch.change(function(){
|
}
|
||||||
drawVideosTableViewElements()
|
|
||||||
})
|
monitorsList.change(debounce(function(){
|
||||||
async function drawVideosTableViewElements(usePreloadedData){
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
var dateRange = getSelectedTime(dateSelector)
|
drawVideosTableViewElements();
|
||||||
var searchQuery = objectTagSearchField.val() || null
|
}, 300));
|
||||||
var startDate = dateRange.startDate
|
|
||||||
var endDate = dateRange.endDate
|
objectTagSearchField.change(debounce(function(){
|
||||||
var monitorId = monitorsList.val()
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
var wantsArchivedVideo = getVideoSetSelected() === 'archive'
|
drawVideosTableViewElements();
|
||||||
var wantCloudVideo = wantCloudVideos()
|
}, 300));
|
||||||
var frameIconsHtml = ''
|
|
||||||
if(!usePreloadedData){
|
cloudVideoCheckSwitch.change(debounce(function(){
|
||||||
loadedVideosTable = (await getVideos({
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
monitorId,
|
drawVideosTableViewElements();
|
||||||
startDate,
|
}, 300));
|
||||||
endDate,
|
|
||||||
searchQuery,
|
// Event listener for the Refresh link styled as a button
|
||||||
archived: wantsArchivedVideo,
|
$('.refresh-data').click(function(e) {
|
||||||
customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
e.preventDefault();
|
||||||
})).videos;
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
$.each(loadedVideosTable,function(n,v){
|
drawVideosTableViewElements();
|
||||||
loadedVideosInMemory[`${monitorId}${v.time}${v.type}`]
|
});
|
||||||
})
|
|
||||||
}
|
async function drawVideosTableViewElements(pageNumber, pageSize, usePreloadedData) {
|
||||||
// for (let i = 0; i < loadedVideosTable.length; i++) {
|
// Set default values if pageNumber and pageSize are not provided
|
||||||
// const file = loadedVideosTable[i]
|
pageNumber = pageNumber || 1;
|
||||||
// const frameUrl = await getSnapshotFromVideoTimeFrame(file.mid,file.time,file.end);
|
pageSize = pageSize || 10;
|
||||||
// file.frameUrl = frameUrl
|
|
||||||
// }
|
var dateRange = getSelectedTime(dateSelector);
|
||||||
videosTableDrawArea.bootstrapTable('destroy')
|
var searchQuery = objectTagSearchField.val() || null;
|
||||||
videosTableDrawArea.bootstrapTable({
|
var startDate = dateRange.startDate;
|
||||||
onPostBody: loadFramesForVideosInView,
|
var endDate = dateRange.endDate;
|
||||||
onPageChange: () => {
|
var monitorId = monitorsList.val();
|
||||||
setTimeout(() => {
|
var wantsArchivedVideo = getVideoSetSelected() === 'archive';
|
||||||
loadFramesForVideosInView()
|
var wantCloudVideo = wantCloudVideos();
|
||||||
},500)
|
|
||||||
|
if (!usePreloadedData) {
|
||||||
|
var result = await getVideos({
|
||||||
|
monitorId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
searchQuery,
|
||||||
|
archived: wantsArchivedVideo,
|
||||||
|
customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
||||||
|
pageSize: pageSize, // Pass pageSize to server
|
||||||
|
currentPage: pageNumber // Pass currentPage to server
|
||||||
|
});
|
||||||
|
loadedVideosTable = result.videos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//videosTableDrawArea.bootstrapTable('destroy');
|
||||||
|
videosTableDrawArea.bootstrapTable({
|
||||||
|
pagination: true,
|
||||||
|
search: true,
|
||||||
|
pageList: [10, 25, 50, 100, 1000, 2000],
|
||||||
|
pageSize: pageSize, // Ensure the current page size is maintained
|
||||||
|
pageNumber: pageNumber, // Ensure the current page number is maintained
|
||||||
|
totalRows: result.total, // Reflect total number of videos
|
||||||
|
onPostBody: loadFramesForVideosInView,
|
||||||
|
onPageChange: (newPageNumber, newPageSize) => {
|
||||||
|
drawVideosTableViewElements(newPageNumber, newPageSize);
|
||||||
|
setTimeout(() => {
|
||||||
|
loadFramesForVideosInView();
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'mid',
|
||||||
|
title: '',
|
||||||
|
checkbox: true,
|
||||||
|
formatter: () => {
|
||||||
|
return {
|
||||||
|
checked: false
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
pagination: true,
|
{
|
||||||
search: true,
|
field: 'image',
|
||||||
pageList: [10, 25, 50, 100, 1000, 2000],
|
title: '',
|
||||||
columns: [
|
},
|
||||||
{
|
{
|
||||||
field: 'mid',
|
field: 'Monitor',
|
||||||
title: '',
|
title: '',
|
||||||
checkbox: true,
|
},
|
||||||
formatter: () => {
|
{
|
||||||
return {
|
field: 'time',
|
||||||
checked: false
|
title: lang['Time'],
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
},
|
field: 'objects',
|
||||||
{
|
title: lang['Objects Found']
|
||||||
field: 'image',
|
},
|
||||||
title: '',
|
{
|
||||||
},
|
field: 'tags',
|
||||||
{
|
title: ''
|
||||||
field: 'Monitor',
|
},
|
||||||
title: '',
|
{
|
||||||
},
|
field: 'size',
|
||||||
{
|
title: ''
|
||||||
field: 'time',
|
},
|
||||||
title: lang['Time'],
|
{
|
||||||
},
|
field: 'buttons',
|
||||||
{
|
title: ''
|
||||||
field: 'objects',
|
}
|
||||||
title: lang['Objects Found']
|
],
|
||||||
},
|
data: loadedVideosTable.map((file) => {
|
||||||
{
|
var isLocalVideo = !wantCloudVideo;
|
||||||
field: 'tags',
|
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`;
|
||||||
title: ''
|
var loadedMonitor = loadedMonitors[file.mid];
|
||||||
},
|
return {
|
||||||
{
|
image: `<div class="video-thumbnail" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-end="${file.end}" data-filename="${file.filename}">
|
||||||
field: 'size',
|
<div class="video-thumbnail-img-block">
|
||||||
title: ''
|
<img class="pop-image cursor-pointer" style="min-width: 100px;min-height: 75px;">
|
||||||
},
|
</div>
|
||||||
{
|
<div class="video-thumbnail-buttons d-flex">
|
||||||
field: 'buttons',
|
<a class="video-thumbnail-button-cell open-snapshot p-3">
|
||||||
title: ''
|
<i class="fa fa-camera"></i>
|
||||||
}
|
</a>
|
||||||
],
|
<a class="video-thumbnail-button-cell preview-video p-3" href="${href}" title="${lang.Play}">
|
||||||
data: loadedVideosTable.map((file) => {
|
<i class="fa fa-play"></i>
|
||||||
var isLocalVideo = !wantCloudVideo
|
</a>
|
||||||
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
|
</div>
|
||||||
var loadedMonitor = loadedMonitors[file.mid]
|
</div>`,
|
||||||
return {
|
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
|
||||||
image: `<div class="video-thumbnail" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-end="${file.end}" data-filename="${file.filename}">
|
mid: file.mid,
|
||||||
<div class="video-thumbnail-img-block">
|
time: `
|
||||||
<img class="pop-image cursor-pointer" style="min-width: 100px;min-height: 75px;">
|
<div>${timeAgo(file.time)}</div>
|
||||||
</div>
|
<div><small><b>${lang.Start} :</b> ${formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>
|
||||||
<div class="video-thumbnail-buttons d-flex">
|
<div><small><b>${lang.End} :</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>`,
|
||||||
<a class="video-thumbnail-button-cell open-snapshot p-3">
|
objects: file.objects,
|
||||||
<i class="fa fa-camera"></i>
|
tags: `
|
||||||
</a>
|
${file.ext ? `<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>` : ''}
|
||||||
<a class="video-thumbnail-button-cell preview-video p-3" href="${href}" title="${lang.Play}">
|
${!isLocalVideo ? `<span class="badge badge-success">${file.type}</span>` : ''}
|
||||||
<i class="fa fa-play"></i>
|
`,
|
||||||
</a>
|
size: convertKbToHumanSize(file.size),
|
||||||
</div>
|
buttons: `
|
||||||
</div>`,
|
<div class="row-info btn-group" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-filename="${file.filename}" data-status="${file.status}" data-type="${file.type}">
|
||||||
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
|
<a class="btn btn-sm btn-default btn-monitor-status-color open-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
|
||||||
mid: file.mid,
|
${isLocalVideo && permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-${file.archive === 1 ? `success status-archived` : `default`} archive-video" title="${lang.Archive}"><i class="fa fa-${file.archive === 1 ? `lock` : `unlock-alt`}"></i></a>` : ''}
|
||||||
time: `
|
<div class="dropdown d-inline-block">
|
||||||
<div>${timeAgo(file.time)}</div>
|
<a class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" data-bs-reference="parent">
|
||||||
<div><small><b>${lang.Start} :</b> ${formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>
|
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||||
<div><small><b>${lang.End} :</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>`,
|
</a>
|
||||||
objects: file.objects,
|
<ul class="dropdown-menu ${definitions.Theme.isDark ? 'dropdown-menu-dark bg-dark' : ''} shadow-lg">
|
||||||
tags: `
|
${buildDefaultVideoMenuItems(file)}
|
||||||
${file.ext ? `<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>` : ''}
|
</ul>
|
||||||
${!isLocalVideo ? `<span class="badge badge-success">${file.type}</span>` : ''}
|
|
||||||
`,
|
|
||||||
size: convertKbToHumanSize(file.size),
|
|
||||||
buttons: `
|
|
||||||
<div class="row-info btn-group" data-mid="${file.mid}" data-ke="${file.ke}" data-time="${file.time}" data-filename="${file.filename}" data-status="${file.status}" data-type="${file.type}">
|
|
||||||
<a class="btn btn-sm btn-default btn-monitor-status-color open-video" href="${href}" title="${lang.Play}"><i class="fa fa-play"></i></a>
|
|
||||||
${isLocalVideo && permissionCheck('video_delete',file.mid) ? `<a class="btn btn-sm btn-${file.archive === 1 ? `success status-archived` : `default`} archive-video" title="${lang.Archive}"><i class="fa fa-${file.archive === 1 ? `lock` : `unlock-alt`}"></i></a>` : ''}
|
|
||||||
<div class="dropdown d-inline-block">
|
|
||||||
<a class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" data-bs-reference="parent">
|
|
||||||
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu ${definitions.Theme.isDark ? 'dropdown-menu-dark bg-dark' : ''} shadow-lg">
|
|
||||||
${buildDefaultVideoMenuItems(file)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`,
|
</div>
|
||||||
}
|
`,
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
function drawPreviewVideo(href){
|
function drawPreviewVideo(href){
|
||||||
videosTablePreviewArea.html(`<video class="video_video" style="width:100%" autoplay controls preload loop src="${href}"></video>`)
|
videosTablePreviewArea.html(`<video class="video_video" style="width:100%" autoplay controls preload loop src="${href}"></video>`)
|
||||||
|
@ -389,6 +419,7 @@ $(document).ready(function(e){
|
||||||
delete(loadedVideosInMemory[`${data.mid}${data.time}${data.type}`])
|
delete(loadedVideosInMemory[`${data.mid}${data.time}${data.type}`])
|
||||||
clearTimeout(redrawTimeout)
|
clearTimeout(redrawTimeout)
|
||||||
redrawTimeout = setTimeout(function(){
|
redrawTimeout = setTimeout(function(){
|
||||||
|
videosTableDrawArea.bootstrapTable('destroy');
|
||||||
drawVideosTableViewElements(true)
|
drawVideosTableViewElements(true)
|
||||||
},2000)
|
},2000)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
(function() {
|
||||||
|
// save these original methods before they are overwritten
|
||||||
|
var proto_initIcon = L.Marker.prototype._initIcon;
|
||||||
|
var proto_setPos = L.Marker.prototype._setPos;
|
||||||
|
|
||||||
|
var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
|
||||||
|
|
||||||
|
L.Marker.addInitHook(function () {
|
||||||
|
var iconOptions = this.options.icon && this.options.icon.options;
|
||||||
|
var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
|
||||||
|
if (iconAnchor) {
|
||||||
|
iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
|
||||||
|
}
|
||||||
|
this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;
|
||||||
|
this.options.rotationAngle = this.options.rotationAngle || 0;
|
||||||
|
|
||||||
|
// Ensure marker keeps rotated during dragging
|
||||||
|
this.on('drag', function(e) { e.target._applyRotation(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
L.Marker.include({
|
||||||
|
_initIcon: function() {
|
||||||
|
proto_initIcon.call(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setPos: function (pos) {
|
||||||
|
proto_setPos.call(this, pos);
|
||||||
|
this._applyRotation();
|
||||||
|
},
|
||||||
|
|
||||||
|
_applyRotation: function () {
|
||||||
|
if(this.options.rotationAngle) {
|
||||||
|
this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;
|
||||||
|
|
||||||
|
if(oldIE) {
|
||||||
|
// for IE 9, use the 2D rotation
|
||||||
|
this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
|
||||||
|
} else {
|
||||||
|
// for modern browsers, prefer the 3D accelerated version
|
||||||
|
this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setRotationAngle: function(angle) {
|
||||||
|
this.options.rotationAngle = angle;
|
||||||
|
this.update();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setRotationOrigin: function(origin) {
|
||||||
|
this.options.rotationOrigin = origin;
|
||||||
|
this.update();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
|
@ -1,5 +1,6 @@
|
||||||
<!-- Core JS Files -->
|
<!-- Core JS Files -->
|
||||||
<script src="<%-window.libURL%>assets/vendor/leaflet/leaflet.js"></script>
|
<script src="<%-window.libURL%>assets/vendor/leaflet/leaflet.js"></script>
|
||||||
|
<script src="<%-window.libURL%>assets/vendor/leaflet/markerRotator.js"></script>
|
||||||
<script src="<%-window.libURL%>assets/vendor/js/jquery-ui.min.js"></script>
|
<script src="<%-window.libURL%>assets/vendor/js/jquery-ui.min.js"></script>
|
||||||
<script src="<%-window.libURL%>assets/vendor/js/pnotify.custom.min.js"></script>
|
<script src="<%-window.libURL%>assets/vendor/js/pnotify.custom.min.js"></script>
|
||||||
<script src="<%-window.libURL%>assets/vendor/js/socket.io.min.js"></script>
|
<script src="<%-window.libURL%>assets/vendor/js/socket.io.min.js"></script>
|
||||||
|
|
Loading…
Reference in New Issue