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",
|
||||
"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": {
|
||||
|
@ -4931,26 +4940,6 @@ module.exports = function(s,config,lang){
|
|||
"placeholder": "3",
|
||||
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": {
|
||||
|
@ -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"',
|
||||
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'],
|
||||
// class: 'cursor-pointer',
|
||||
|
|
|
@ -385,6 +385,7 @@
|
|||
"Started": "Gestartet",
|
||||
"Status Indicator": "Statusanzeige",
|
||||
"Stop URL": "Stop-URL",
|
||||
"Storage Class": "Speicherklassen",
|
||||
"Stream": "Stream",
|
||||
"Stream Flags": "Stream-Flags",
|
||||
"Stream Timestamp": "Stream-Timestamp",
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"Session Key": "Session Key",
|
||||
"Active Monitors": "Active Monitors",
|
||||
"Storage Use": "Storage Use",
|
||||
"Storage Class": "Storage Class",
|
||||
"Use Raw Snapshot": "Use Raw Snapshot",
|
||||
"Failed to Edit Account": "Failed to Edit Account",
|
||||
"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.",
|
||||
"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.",
|
||||
"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",
|
||||
"Stream in Background": "Stream in Background",
|
||||
"Carousel in Background": "Carousel in Background",
|
||||
|
@ -1161,6 +1163,7 @@
|
|||
"startUpText3": "waiting to give unfinished video check some time. 3 seconds.",
|
||||
"startUpText4": "Starting Monitors... Please Wait...",
|
||||
"startUpText5": "Shinobi is ready.",
|
||||
"notReadyYet": "Shinobi is not ready yet to do this.",
|
||||
"startUpText6": "Orphaned Videos Found and Inserted",
|
||||
"Migrator": "Migrator",
|
||||
"Thumbnail": "Thumbnail",
|
||||
|
@ -1269,6 +1272,7 @@
|
|||
"hwaccel": "Acceleration Engine",
|
||||
"hwaccel_vcodec": "Video Decoder",
|
||||
"hwaccel_device": "HWAccel Device",
|
||||
"hwaccel_format": "HWAccel Format",
|
||||
"Get Logs to Client": "Get Logs to Client",
|
||||
"Hardware Accelerated": "Hardware Accelerated",
|
||||
"Accelerator": "Accelerator",
|
||||
|
|
|
@ -504,6 +504,7 @@
|
|||
"Stop": "Arrêt",
|
||||
"Stop URL": "URL d'arrêt",
|
||||
"Storage Location": "Emplacement de stockage",
|
||||
"Storage Class": "Classes de Stockage",
|
||||
"Stream": "Flux",
|
||||
"Stream Channel": "Canal du flux de données",
|
||||
"Stream Flags": "Etiquettes du flux",
|
||||
|
|
|
@ -943,6 +943,7 @@
|
|||
"Stopping": "Fermarsi",
|
||||
"Storage Location": "Posizione di archiviazione",
|
||||
"Storage Use": "Uso di archiviazione",
|
||||
"Storage Class": "Classi di Archiviazione",
|
||||
"Stream": "Flusso",
|
||||
"Stream Channel": "Canale di flusso",
|
||||
"Stream Flags": "Flag di streaming",
|
||||
|
|
|
@ -1489,6 +1489,7 @@
|
|||
"Stopping": "停止中",
|
||||
"Storage Location": "Storage Location",
|
||||
"Storage Use": "使用ストレージ",
|
||||
"Storage Class": "ストレージクラス",
|
||||
"Stream Channel": "Stream Channel",
|
||||
"Stream Channels": "Stream Channels",
|
||||
"Stream Flags": "ストリームフラグ",
|
||||
|
|
|
@ -169,7 +169,7 @@ module.exports = function(s,config,lang){
|
|||
activeSession &&
|
||||
(
|
||||
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){
|
||||
|
@ -218,6 +218,13 @@ module.exports = function(s,config,lang){
|
|||
onFail()
|
||||
}
|
||||
}
|
||||
s.authPromise = function(params,res,req){
|
||||
return new Promise((resolve) => {
|
||||
s.auth(params, (user) => {
|
||||
resolve(user)
|
||||
},res,req)
|
||||
})
|
||||
}
|
||||
//super user authentication handler
|
||||
s.superAuth = function(params,callback,res,req){
|
||||
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{
|
||||
var config = require(s.location.config)
|
||||
}catch(err){
|
||||
console.log('FAILED TO OPEN CONFIGURATION FILE')
|
||||
console.log('CHECK SYNTAX!')
|
||||
var config = {}
|
||||
}
|
||||
if(!config.productType){
|
||||
|
|
15
libs/cron.js
15
libs/cron.js
|
@ -28,9 +28,24 @@ module.exports = (s,config,lang) => {
|
|||
case's.deleteVideo':
|
||||
s.deleteVideo(data.file)
|
||||
break;
|
||||
case's.deleteCloudVideo':
|
||||
s.deleteVideo(data.file)
|
||||
break;
|
||||
case's.deleteFileBinEntry':
|
||||
s.deleteFileBinEntry(data.file)
|
||||
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':
|
||||
function doOnMain(){
|
||||
s.setDiskUsedForGroup(data.ke,data.size,data.target || undefined)
|
||||
|
|
|
@ -4,7 +4,7 @@ const moment = require('moment');
|
|||
const exec = require('child_process').exec;
|
||||
const spawn = require('child_process').spawn;
|
||||
const { parentPort, isMainThread, workerData } = require('worker_threads');
|
||||
const config = workerData
|
||||
const config = workerData;
|
||||
process.on('uncaughtException', function (err) {
|
||||
errorLog('uncaughtException',err);
|
||||
});
|
||||
|
@ -96,6 +96,18 @@ function beginProcessing(){
|
|||
const deleteFileBinEntry = (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) => {
|
||||
postMessage({f:'s.setDiskUsedForGroup', ke: groupKey, size: size, target: target, videoRow: videoRow})
|
||||
}
|
||||
|
@ -546,6 +558,9 @@ function beginProcessing(){
|
|||
overlapLocks[v.ke] = true
|
||||
v.d = JSON.parse(v.details);
|
||||
try{
|
||||
debugLog('--- Running Pre Extenders')
|
||||
onCronGroupBeforeProcessed(v)
|
||||
onCronGroupBeforeProcessedAwaited(v)
|
||||
await deleteOldVideos(v)
|
||||
debugLog('--- deleteOldVideos Complete')
|
||||
await deleteOldTimelapseFrames(v)
|
||||
|
@ -562,6 +577,9 @@ function beginProcessing(){
|
|||
debugLog('--- checkFilterRules Complete')
|
||||
await deleteRowsWithNoVideo(v)
|
||||
debugLog('--- deleteRowsWithNoVideo Complete')
|
||||
debugLog('--- Running Post Extenders')
|
||||
onCronGroupProcessed(v)
|
||||
onCronGroupProcessedAwaited(v)
|
||||
}catch(err){
|
||||
normalLog(`Failed to Complete User : ${v.mail}`)
|
||||
normalLog(err)
|
||||
|
|
|
@ -175,9 +175,9 @@ module.exports = function(s,config){
|
|||
}
|
||||
const getDatabaseRows = function(options,callback){
|
||||
//current cant handle `end` time
|
||||
var whereQuery = [
|
||||
var whereQuery = options.groupKey ? [
|
||||
['ke','=',options.groupKey],
|
||||
]
|
||||
] : []
|
||||
const monitorRestrictions = options.monitorRestrictions
|
||||
var frameLimit = options.limit
|
||||
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 {
|
||||
knexQuery: knexQuery,
|
||||
knexQueryPromise: knexQueryPromise,
|
||||
|
@ -499,5 +529,7 @@ module.exports = function(s,config){
|
|||
alterColumn,
|
||||
addColumn,
|
||||
isMySQL,
|
||||
sqlDate,
|
||||
dateSubtract,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,11 +39,12 @@ module.exports = (s,config,lang) => {
|
|||
async function saveImageFromEvent(options,frameBuffer){
|
||||
const monitorId = options.mid || options.id
|
||||
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 objectsFound = options.matrices
|
||||
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 filename = s.formattedTime(eventTime) + '.jpg'
|
||||
const location = timelapseRecordingDirectory + currentDate + '/'
|
||||
|
@ -79,9 +80,8 @@ module.exports = (s,config,lang) => {
|
|||
var newString = string + ''
|
||||
var d = Object.assign(eventData,addOps)
|
||||
var detailString = s.stringJSON(d.details)
|
||||
var tag = detailString.matrices
|
||||
&& detailString.matrices[0]
|
||||
&& detailString.matrices[0].tag;
|
||||
var firstMatrix = d.details.matrices ? d.details.matrices[0] : null;
|
||||
var tag = firstMatrix ? firstMatrix.tag : '';
|
||||
newString = newString
|
||||
.replace(/{{CONFIDENCE}}/g,d.details.confidence)
|
||||
.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(/{{GROUP_KEY}}/g,d.ke)
|
||||
.replace(/{{DETAILS}}/g,detailString);
|
||||
if(tag){
|
||||
if(firstMatrix && tag){
|
||||
newString = newString.replace(/{{TAG}}/g,tag)
|
||||
}
|
||||
if(d.details.confidence){
|
||||
if(d.details.confidence || firstMatrix){
|
||||
newString = newString
|
||||
.replace(/{{CONFIDENCE}}/g,d.details.confidence)
|
||||
.replace(/{{CONFIDENCE}}/g,d.details.confidence || firstMatrix.confidence)
|
||||
}
|
||||
if(newString.includes("REASON")) {
|
||||
if(d.details.reason) {
|
||||
if(d.details.reason && newString.includes("REASON")) {
|
||||
newString = newString
|
||||
.replace(/{{REASON}}/g, d.details.reason)
|
||||
}
|
||||
}
|
||||
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 //////
|
||||
createExtension(`onSocketAuthentication`)
|
||||
createExtension(`onUserLog`)
|
||||
|
@ -59,6 +87,11 @@ module.exports = function(s,config){
|
|||
createExtension(`onSubscriptionCheck`)
|
||||
createExtension(`onDataPortMessage`)
|
||||
createExtension(`onHttpRequestUpgrade`,null,true)
|
||||
/////// CRON ////////
|
||||
createExtension(`onCronGroupProcessed`)
|
||||
createExtension(`onCronGroupProcessedAwaited`)
|
||||
createExtension(`onCronGroupBeforeProcessed`)
|
||||
createExtension(`onCronGroupBeforeProcessedAwaited`)
|
||||
/////// VIDEOS ////////
|
||||
createExtension(`insertCompletedVideoExtender`,`insertCompletedVideoExtensions`)
|
||||
createExtension(`onEventBasedRecordingComplete`)
|
||||
|
|
|
@ -180,6 +180,9 @@ module.exports = (s,config,lang) => {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(input.hwaccel_format){
|
||||
inputFlags.push(`-hwaccel_output_format ${input.hwaccel_format}`)
|
||||
}
|
||||
}
|
||||
//custom - input flags
|
||||
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}`)
|
||||
}
|
||||
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'){
|
||||
streamFilters.push(`fps=${videoFps}`)
|
||||
}
|
||||
|
@ -362,6 +365,9 @@ module.exports = (s,config,lang) => {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(e.details.hwaccel_format){
|
||||
inputFlags.push(`-hwaccel_output_format ${e.details.hwaccel_format}`)
|
||||
}
|
||||
}
|
||||
inputFlags.push(`-loglevel ${logLevel}`)
|
||||
//add main input
|
||||
|
@ -415,8 +421,13 @@ module.exports = (s,config,lang) => {
|
|||
streamFlags.push(`-an`)
|
||||
}
|
||||
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.hwaccel_format) {
|
||||
streamFilters.push(',')
|
||||
}
|
||||
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'){
|
||||
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(videoFps && streamType === 'mjpeg' || streamType === 'b64' || videoFps && !videoCodecisCopy){
|
||||
streamFilters.push(`fps=${videoFps}`)
|
||||
|
|
|
@ -27,8 +27,12 @@ module.exports = function(s,config,lang){
|
|||
getMonitorConfiguration,
|
||||
copyMonitorConfiguration,
|
||||
checkObjectsInMonitorDetails,
|
||||
isGroupBelowMaxMonitorCount,
|
||||
} = require('./monitor/utils.js')(s,config,lang)
|
||||
const {
|
||||
canAddMoreMonitors,
|
||||
sanitizeMonitorConfig,
|
||||
isGroupBelowMaxMonitorCount,
|
||||
} = require('./checker/utils.js')(s,config,lang)
|
||||
s.initiateMonitorObject = function(e){
|
||||
if(!s.group[e.ke]){s.group[e.ke]={}};
|
||||
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'
|
||||
const snapRawFilters = monitor.details.cust_snap_raw
|
||||
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{
|
||||
await fs.promises.mkdir(streamDir, {recursive: true}, (err) => {s.debugLog(err)})
|
||||
}catch(err){
|
||||
|
@ -546,10 +550,9 @@ module.exports = function(s,config,lang){
|
|||
var endData = {
|
||||
ok: false
|
||||
}
|
||||
if(!form.mid){
|
||||
endData.msg = lang['No Monitor ID Present in Form']
|
||||
if(!form.mid || !s.timeReady){
|
||||
endData.msg = !s.timeReady ? lang.notReadyYet : lang['No Monitor ID Present in Form']
|
||||
if(callback)callback(endData);
|
||||
resolve(endData)
|
||||
return
|
||||
}
|
||||
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 systemMax = canAddMoreMonitors();
|
||||
const groupMax = isGroupBelowMaxMonitorCount(form.ke);
|
||||
const canDoTheDo = systemMax && groupMax;
|
||||
var affectMonitor = false
|
||||
var monitorQuery = {}
|
||||
var txData = {
|
||||
|
@ -610,7 +616,7 @@ module.exports = function(s,config,lang){
|
|||
]
|
||||
})
|
||||
affectMonitor = true
|
||||
}else if(isGroupBelowMaxMonitorCount(form.ke)){
|
||||
}else if(canDoTheDo){
|
||||
txData.new = true
|
||||
Object.keys(form).forEach(function(v){
|
||||
if(form[v] && form[v] !== ''){
|
||||
|
@ -631,7 +637,7 @@ module.exports = function(s,config,lang){
|
|||
}else{
|
||||
txData.f = 'monitor_edit_failed'
|
||||
txData.ff = 'max_reached'
|
||||
endData.msg = user.lang.monitorEditFailedMaxReached
|
||||
endData.msg = !systemMax ? user.lang.monitorEditFailedMaxReachedUnactivated : user.lang.monitorEditFailedMaxReached
|
||||
}
|
||||
if(affectMonitor === true){
|
||||
form.details = JSON.parse(form.details)
|
||||
|
@ -650,10 +656,12 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
s.tx(txData,'GRP_'+form.ke)
|
||||
if(callback)callback(!endData.ok,endData);
|
||||
let monitorConfig = copyMonitorConfiguration(form.ke,form.mid)
|
||||
s.onMonitorSaveExtensions.forEach(function(extender){
|
||||
extender(monitorConfig,form,endData)
|
||||
})
|
||||
if(monitorExists || canDoTheDo){
|
||||
let monitorConfig = copyMonitorConfiguration(form.ke,form.mid)
|
||||
s.onMonitorSaveExtensions.forEach(function(extender){
|
||||
extender(monitorConfig,form,endData)
|
||||
})
|
||||
}
|
||||
return endData
|
||||
}
|
||||
s.camera = async (selectedMode,e,cn) => {
|
||||
|
|
|
@ -219,7 +219,7 @@ module.exports = (s,config,lang) => {
|
|||
})
|
||||
}
|
||||
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})
|
||||
snapProcess.stderr.on('data',function(data){
|
||||
// s.debugLog(data.toString())
|
||||
|
@ -674,6 +674,8 @@ module.exports = (s,config,lang) => {
|
|||
mid: monitorId,
|
||||
});
|
||||
}
|
||||
delete(s.group[groupKey].activeMonitors[monitorId]);
|
||||
delete(s.group[groupKey].rawMonitorConfigurations[monitorId]);
|
||||
response.msg = `${lang.monitorDeleted} ${lang.byUser} : ${userId}`
|
||||
}catch(err){
|
||||
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){
|
||||
d.videoEndDate = stringToSqlTime(d.endDate)
|
||||
}
|
||||
var getVideos = function(callback){
|
||||
var videoWhereQuery = [
|
||||
['ke','=',cn.ke],
|
||||
]
|
||||
if(d.videoStartDate || d.videoEndDate){
|
||||
if(!d.videoStartDateOperator||d.videoStartDateOperator==''){
|
||||
d.videoStartDateOperator='>='
|
||||
}
|
||||
if(!d.videoEndDateOperator||d.videoEndDateOperator==''){
|
||||
d.videoEndDateOperator='<='
|
||||
}
|
||||
switch(true){
|
||||
case(d.videoStartDate && d.videoStartDate !== '' && d.videoEndDate && d.videoEndDate !== ''):
|
||||
videoWhereQuery.push(['time',d.videoStartDateOperator,d.videoStartDate])
|
||||
videoWhereQuery.push(['end',d.videoEndDateOperator,d.videoEndDate])
|
||||
break;
|
||||
case(d.videoStartDate && d.videoStartDate !== ''):
|
||||
videoWhereQuery.push(['time',d.videoStartDateOperator,d.videoStartDate])
|
||||
break;
|
||||
case(d.videoEndDate && d.videoEndDate !== ''):
|
||||
videoWhereQuery.push(['end',d.videoEndDateOperator,d.videoEndDate])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(monitorRestrictions.length > 0){
|
||||
videoWhereQuery.push(monitorRestrictions)
|
||||
}
|
||||
s.knexQuery({
|
||||
action: "select",
|
||||
columns: "*",
|
||||
table: videoSet === 'cloud' ? `Cloud Videos` : "Videos",
|
||||
where: videoWhereQuery,
|
||||
orderBy: ['time','desc'],
|
||||
limit: d.videoLimit || '100'
|
||||
},(err,r) => {
|
||||
if(err){
|
||||
console.error(err)
|
||||
setTimeout(function(){
|
||||
callback({total:0,limit:d.videoLimit,videos:[]})
|
||||
},2000)
|
||||
}else{
|
||||
s.buildVideoLinks(r,{
|
||||
videoParam : videoSet === 'cloud' ? `cloudVideos` : "videos",
|
||||
auth : cn.auth
|
||||
})
|
||||
callback({total:r.length,limit:d.videoLimit,videos:r})
|
||||
}
|
||||
})
|
||||
}
|
||||
var getVideos = function(callback) {
|
||||
var videoWhereQuery = [
|
||||
['ke','=',cn.ke],
|
||||
];
|
||||
|
||||
// Add filtering logic here (startDate, endDate, etc.)
|
||||
if(d.videoStartDate || d.videoEndDate) {
|
||||
if(!d.videoStartDateOperator || d.videoStartDateOperator == '') {
|
||||
d.videoStartDateOperator = '>='
|
||||
}
|
||||
if(!d.videoEndDateOperator || d.videoEndDateOperator == '') {
|
||||
d.videoEndDateOperator = '<='
|
||||
}
|
||||
switch(true) {
|
||||
case(d.videoStartDate && d.videoStartDate !== '' && d.videoEndDate && d.videoEndDate !== ''):
|
||||
videoWhereQuery.push(['time', d.videoStartDateOperator, d.videoStartDate])
|
||||
videoWhereQuery.push(['end', d.videoEndDateOperator, d.videoEndDate])
|
||||
break;
|
||||
case(d.videoStartDate && d.videoStartDate !== ''):
|
||||
videoWhereQuery.push(['time', d.videoStartDateOperator, d.videoStartDate])
|
||||
break;
|
||||
case(d.videoEndDate && d.videoEndDate !== ''):
|
||||
videoWhereQuery.push(['end', d.videoEndDateOperator, d.videoEndDate])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(monitorRestrictions.length > 0) {
|
||||
videoWhereQuery.push(monitorRestrictions)
|
||||
}
|
||||
|
||||
// Implementing pagination
|
||||
var pageSize = parseInt(d.pageSize) || 10; // Default page size
|
||||
var currentPage = parseInt(d.currentPage) || 1; // Default to page 1
|
||||
var offset = (currentPage - 1) * pageSize;
|
||||
|
||||
s.knexQuery({
|
||||
action: "select",
|
||||
columns: "*",
|
||||
table: videoSet === 'cloud' ? `Cloud Videos` : "Videos",
|
||||
where: videoWhereQuery,
|
||||
orderBy: ['time','desc'],
|
||||
limit: pageSize, // Limiting the number of rows returned
|
||||
offset: offset // Skipping the previous pages' rows
|
||||
},(err,r) => {
|
||||
if(err) {
|
||||
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){
|
||||
getEvents(function(events){
|
||||
tx({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// https://us-east-1.console.aws.amazon.com/iamv2/home#/users
|
||||
|
||||
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){
|
||||
const genericRequest = async (groupKey,requestOptions) => {
|
||||
|
@ -123,7 +123,8 @@ module.exports = function(s,config,lang){
|
|||
Bucket: s.group[groupKey].init.aws_s3_bucket,
|
||||
Key: saveLocation,
|
||||
Body: fileStream,
|
||||
ContentType: 'video/'+e.ext
|
||||
ContentType: 'video/'+e.ext,
|
||||
StorageClass: s.group[groupKey].init.aws_storage_class || StorageClass.STANDARD
|
||||
}).then((response) => {
|
||||
if(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,
|
||||
"name": "detail=aws_s3_log",
|
||||
|
|
|
@ -787,14 +787,28 @@ module.exports = function(s,config,lang,app,io){
|
|||
return
|
||||
}
|
||||
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({
|
||||
action: "select",
|
||||
columns: "*",
|
||||
table: "Monitors",
|
||||
where: [
|
||||
['ke','=',groupKey],
|
||||
monitorRestrictions
|
||||
]
|
||||
where: whereQuery
|
||||
},(err,r) => {
|
||||
r.forEach(function(v,n){
|
||||
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 = {
|
||||
onLoadFieldsExtensions: [],
|
||||
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)
|
||||
}
|
||||
}
|
||||
function closeLiveGridPlayers(monitors, killElement){
|
||||
$.each(monitors,function(n,v){
|
||||
monitorWatchOnLiveGrid(v.mid, killElement)
|
||||
})
|
||||
}
|
||||
function monitorWatchOnLiveGrid(monitorId, watchOff){
|
||||
return mainSocket.f({f:'monitor',ff:watchOff ? 'watch_off' : 'watch_on',id: monitorId})
|
||||
}
|
||||
|
@ -664,13 +669,20 @@ function monitorsWatchOnLiveGrid(monitorIds, watchOff){
|
|||
monitorWatchOnLiveGrid(monitorId, watchOff)
|
||||
})
|
||||
}
|
||||
function callMonitorToLiveGrid(v){
|
||||
function callMonitorToLiveGrid(v, justTry){
|
||||
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})
|
||||
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(){
|
||||
$.getJSON(getApiPrefix(`monitor`),function(data){
|
||||
$.each(data,function(n,v){
|
||||
|
@ -1279,8 +1291,8 @@ $(document).ready(function(e){
|
|||
var monitorId = d.mid || d.id
|
||||
var loadedMonitor = loadedMonitors[monitorId]
|
||||
var subStreamChannel = d.subStreamChannel
|
||||
var monitorsPerRow = cycleLiveOptions ? cycleLiveOptions.monitorsPerRow : null;
|
||||
var monitorHeight = cycleLiveOptions ? cycleLiveOptions.monitorHeight : null;
|
||||
var monitorsPerRow = null;
|
||||
var monitorHeight = null;
|
||||
if(!loadedMonitor.subStreamChannel && loadedMonitor.details.stream_type === 'useSubstream'){
|
||||
toggleSubStream(monitorId,function(){
|
||||
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel,monitorsPerRow,monitorHeight)
|
||||
|
@ -1428,4 +1440,9 @@ $(document).ready(function(e){
|
|||
})
|
||||
}
|
||||
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 cleanup(){
|
||||
document.removeEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||
document.removeEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||
delete(keyShortcuts['liveGridUtils'])
|
||||
}
|
||||
if(enable){
|
||||
let isKeyPressed = false;
|
||||
function handleKeyboard(event){
|
||||
if (isKeyPressed) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
switch(event.code){
|
||||
case 'Enter':
|
||||
addMarkAsEventToAllOpenMonitors()
|
||||
break;
|
||||
}
|
||||
}
|
||||
function handleKeyup(event) {
|
||||
isKeyPressed = false;
|
||||
}
|
||||
keyShortcuts['liveGridUtils'] = {
|
||||
keydown: handleKeyboard,
|
||||
keyup: handleKeyup,
|
||||
}
|
||||
document.addEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||
document.addEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||
}else{
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
addOnTabOpen('liveGrid', function () {
|
||||
keyShortcutsForLiveGridUtils(true)
|
||||
})
|
||||
addOnTabReopen('liveGrid', function () {
|
||||
keyShortcutsForLiveGridUtils(true)
|
||||
})
|
||||
addOnTabAway('liveGrid', function () {
|
||||
keyShortcutsForLiveGridUtils(false)
|
||||
})
|
||||
// function keyShortcutsForLiveGridUtils(enable) {
|
||||
// function cleanup(){
|
||||
// document.removeEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||
// document.removeEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||
// delete(keyShortcuts['liveGridUtils'])
|
||||
// }
|
||||
// if(enable){
|
||||
// let isKeyPressed = false;
|
||||
// function handleKeyboard(event){
|
||||
// if (isKeyPressed) {
|
||||
// return;
|
||||
// }
|
||||
// event.preventDefault();
|
||||
// switch(event.code){
|
||||
// case 'Enter':
|
||||
// addMarkAsEventToAllOpenMonitors()
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// function handleKeyup(event) {
|
||||
// isKeyPressed = false;
|
||||
// }
|
||||
// keyShortcuts['liveGridUtils'] = {
|
||||
// keydown: handleKeyboard,
|
||||
// keyup: handleKeyup,
|
||||
// }
|
||||
// document.addEventListener('keydown', keyShortcuts['liveGridUtils'].keydown);
|
||||
// document.addEventListener('keyup', keyShortcuts['liveGridUtils'].keyup);
|
||||
// }else{
|
||||
// cleanup()
|
||||
// }
|
||||
// }
|
||||
// addOnTabOpen('liveGrid', function () {
|
||||
// keyShortcutsForLiveGridUtils(true)
|
||||
// })
|
||||
// addOnTabReopen('liveGrid', function () {
|
||||
// keyShortcutsForLiveGridUtils(true)
|
||||
// })
|
||||
// addOnTabAway('liveGrid', function () {
|
||||
// keyShortcutsForLiveGridUtils(false)
|
||||
// })
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var loadedMap;
|
||||
$(document).ready(function(){
|
||||
var theBlock = $('#tab-monitorMap')
|
||||
var theMap = $('#monitor-map-canvas')
|
||||
var loadedMap;
|
||||
function loadPopupVideoList(monitor){
|
||||
var groupKey = monitor.ke
|
||||
var monitorId = monitor.mid
|
||||
|
|
|
@ -62,6 +62,7 @@ function generateDefaultMonitorSettings(){
|
|||
"hwaccel": "auto",
|
||||
"hwaccel_vcodec": "",
|
||||
"hwaccel_device": "",
|
||||
"hwaccel_format": "",
|
||||
"use_coprocessor": null,
|
||||
"stream_type": "hls",
|
||||
"stream_flv_type": "http",
|
||||
|
@ -274,6 +275,7 @@ function generateDefaultMonitorSettings(){
|
|||
"hwaccel": null,
|
||||
"hwaccel_vcodec": "",
|
||||
"hwaccel_device": "",
|
||||
"hwaccel_format": "",
|
||||
"cust_input": ""
|
||||
},
|
||||
"output": {
|
||||
|
@ -412,6 +414,7 @@ var copyMonitorSettingsToSelected = function(monitorConfig){
|
|||
monitor.details.hwaccel = monitorDetails.hwaccel
|
||||
monitor.details.hwaccel_vcodec = monitorDetails.hwaccel_vcodec
|
||||
monitor.details.hwaccel_device = monitorDetails.hwaccel_device
|
||||
monitor.details.hwaccel_format = monitorDetails.hwaccel_format
|
||||
}else{
|
||||
monitor = Object.assign({},loadedMonitors[id]);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ $(document).ready(function(e){
|
|||
var monitorSettingsMapOptionsEl = $('#monitor-settings-geolocation-options')
|
||||
var monitorSettingsMapOptionsElOptions = monitorSettingsMapOptionsEl.find('[map-option]')
|
||||
var editorForm = monitorEditorWindow.find('form')
|
||||
var loadedMap;
|
||||
var mapInWindow;
|
||||
var monitorMapMarker;
|
||||
var monitorMapMarkerFov;
|
||||
function setAdditionalControls(options){
|
||||
|
@ -34,14 +34,14 @@ $(document).ready(function(e){
|
|||
fov,
|
||||
range,
|
||||
} = 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', {
|
||||
maxZoom: 19,
|
||||
}).addTo(loadedMap);
|
||||
}).addTo(mapInWindow);
|
||||
monitorMapMarker = L.marker([lat, lng], {
|
||||
title: monitor ? `${monitor.name} (${monitor.host})` : null,
|
||||
draggable: true,
|
||||
}).addTo(loadedMap);
|
||||
}).addTo(mapInWindow);
|
||||
monitorMapMarker.on('dragend', function(){
|
||||
setGeolocationFieldValue()
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ $(document).ready(function(e){
|
|||
var markerDetails = getMapMarkerDetails();
|
||||
setMapMarkerFov(monitorMapMarkerFov,markerDetails)
|
||||
});
|
||||
loadedMap.on('zoomend', function(){
|
||||
mapInWindow.on('zoomend', function(){
|
||||
setGeolocationFieldValue()
|
||||
});
|
||||
setAdditionalControls({
|
||||
|
@ -57,7 +57,7 @@ $(document).ready(function(e){
|
|||
fov,
|
||||
range,
|
||||
})
|
||||
monitorMapMarkerFov = drawMapMarkerFov(loadedMap,{
|
||||
monitorMapMarkerFov = drawMapMarkerFov(mapInWindow,{
|
||||
lat,
|
||||
lng,
|
||||
direction,
|
||||
|
@ -71,8 +71,8 @@ $(document).ready(function(e){
|
|||
})
|
||||
}
|
||||
function unloadMap(){
|
||||
loadedMap.remove();
|
||||
loadedMap = null;
|
||||
mapInWindow.remove();
|
||||
mapInWindow = null;
|
||||
}
|
||||
function getMapOptions(){
|
||||
var options = {}
|
||||
|
@ -86,7 +86,7 @@ $(document).ready(function(e){
|
|||
}
|
||||
function getMapMarkerDetails(){
|
||||
var pos = monitorMapMarker.getLatLng()
|
||||
var zoom = loadedMap.getZoom();
|
||||
var zoom = mapInWindow.getZoom();
|
||||
var {
|
||||
direction,
|
||||
fov,
|
||||
|
|
|
@ -110,7 +110,7 @@ $(document).ready(function(){
|
|||
async function loopOnGaps(monitorId){
|
||||
for (let i = 0; i < gaps.length; i++) {
|
||||
var range = gaps[i]
|
||||
videos.push(...(await getVideos({
|
||||
var videosFound = (await getVideos({
|
||||
monitorId,
|
||||
startDate: range[0],
|
||||
endDate: range[1],
|
||||
|
@ -118,7 +118,9 @@ $(document).ready(function(){
|
|||
searchQuery,
|
||||
// archived: false,
|
||||
// customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
||||
},null,dontShowDetectionOnTimeline)).videos);
|
||||
},null,dontShowDetectionOnTimeline)).videos;
|
||||
videos.push(...videosFound);
|
||||
executeExtender('timelineGetVideosByMonitor', [monitorId, videosFound])
|
||||
}
|
||||
}
|
||||
if(monitorIds && monitorIds.length > 0){
|
||||
|
@ -148,6 +150,7 @@ $(document).ready(function(){
|
|||
setLoadingMask(true)
|
||||
timeStripListOfQueries.push(...gaps)
|
||||
var videos = await getVideosInGaps(gaps,timeStripSelectedMonitors)
|
||||
executeExtender('timelineGetVideos', [videos])
|
||||
videos = addVideoBeforeAndAfter(videos)
|
||||
loadedVideosOnTimeStrip.push(...videos)
|
||||
if(currentVideosLength !== loadedVideosOnTimeStrip.length)addTimelineItems(loadedVideosOnTimeStrip);
|
||||
|
@ -293,6 +296,7 @@ $(document).ready(function(){
|
|||
timeStripActionWithPausePlay().then((timeChanging) => {
|
||||
if(!timeChanging){
|
||||
resetTimeline(clickTime)
|
||||
executeExtender('timelineTimeChange', [clickTime])
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -313,6 +317,7 @@ $(document).ready(function(){
|
|||
setTimeout(() => {
|
||||
timeChanging = false
|
||||
getAndDrawVideosToTimeline(clickTime)
|
||||
executeExtender('timelineRangeChanged', [properties.start, properties.end, getTimestripDate()])
|
||||
},500)
|
||||
},300)
|
||||
})
|
||||
|
@ -641,6 +646,7 @@ $(document).ready(function(){
|
|||
addition += (msSpeed * timelineSpeed);
|
||||
newTime = new Date(currentDate + addition)
|
||||
setTickDate(newTime);
|
||||
executeExtender('timelineTimeChange', [newTime])
|
||||
// setTimeOfCanvasVideos(newTime)
|
||||
}, msSpeed)
|
||||
timeStripVisTickMovementIntervalSecond = setInterval(function() {
|
||||
|
@ -911,9 +917,25 @@ $(document).ready(function(){
|
|||
onSelectedMonitorChange()
|
||||
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(){
|
||||
var askToLoad = isAllMonitorsSelected(50)
|
||||
if(askToLoad){
|
||||
if(!window.skipTimelineAgree && askToLoad){
|
||||
$.confirm.create({
|
||||
title: lang.tooManyMonitorsSelected,
|
||||
body: lang.performanceMayBeAffected,
|
||||
|
@ -931,6 +953,7 @@ $(document).ready(function(){
|
|||
}else{
|
||||
refreshTimeline()
|
||||
}
|
||||
window.skipTimelineAgree = false;
|
||||
}
|
||||
function monitorSelectorController(){
|
||||
var el = $(this)
|
||||
|
@ -1072,4 +1095,8 @@ $(document).ready(function(){
|
|||
if(currentOptions.dontShowDetectionOnTimeline === '1'){
|
||||
timeStripDontShowDetectionToggle()
|
||||
}
|
||||
addExtender('timelineTimeChange')
|
||||
addExtender('timelineRangeChanged')
|
||||
addExtender('timelineGetVideos')
|
||||
addExtender('timelineGetVideosByMonitor')
|
||||
})
|
||||
|
|
|
@ -79,35 +79,69 @@ function createVideoLinks(video,options){
|
|||
video.details = details
|
||||
return video
|
||||
}
|
||||
function applyDataListToVideos(videos,events,keyName,reverseList){
|
||||
var updatedVideos = videos.concat([])
|
||||
var currentEvents = events.concat([])
|
||||
updatedVideos.forEach(function(video){
|
||||
var videoEvents = []
|
||||
currentEvents.forEach(function(theEvent,index){
|
||||
var startTime = new Date(video.time)
|
||||
var endTime = new Date(video.end)
|
||||
var eventTime = new Date(theEvent.time)
|
||||
if(theEvent.mid === video.mid && eventTime >= startTime && eventTime <= endTime){
|
||||
videoEvents.push(theEvent)
|
||||
currentEvents.splice(index, 1)
|
||||
}
|
||||
})
|
||||
if(reverseList)videoEvents = videoEvents.reverse()
|
||||
video[keyName || 'events'] = videoEvents
|
||||
})
|
||||
return updatedVideos
|
||||
function applyDataListToVideos(videos, events, keyName, reverseList) {
|
||||
const eventMap = new Map();
|
||||
|
||||
// Build a map of events by monitor ID
|
||||
events.forEach(event => {
|
||||
if (!eventMap.has(event.mid)) {
|
||||
eventMap.set(event.mid, []);
|
||||
}
|
||||
eventMap.get(event.mid).push(event);
|
||||
});
|
||||
|
||||
// Attach events 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();
|
||||
|
||||
video[keyName || 'events'] = matchedEvents;
|
||||
});
|
||||
|
||||
return videos;
|
||||
}
|
||||
function applyTimelapseFramesListToVideos(videos,events,keyName,reverseList){
|
||||
var thisApiPrefix = getApiPrefix() + '/timelapse/' + $user.ke + '/'
|
||||
var newVideos = applyDataListToVideos(videos,events,keyName,reverseList)
|
||||
newVideos.forEach(function(video){
|
||||
video.timelapseFrames.forEach(function(row){
|
||||
var apiURL = thisApiPrefix + row.mid
|
||||
row.href = libURL + apiURL + '/' + row.filename.split('T')[0] + '/' + row.filename
|
||||
})
|
||||
})
|
||||
return newVideos
|
||||
function applyTimelapseFramesListToVideos(videos, events, keyName, reverseList) {
|
||||
const thisApiPrefix = `${getApiPrefix()}/timelapse/${$user.ke}/`;
|
||||
const eventMap = new Map();
|
||||
|
||||
// Build a map of events by monitor ID
|
||||
events.forEach(event => {
|
||||
if (!eventMap.has(event.mid)) {
|
||||
eventMap.set(event.mid, []);
|
||||
}
|
||||
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) {
|
||||
var startTime = video.time;
|
||||
|
|
|
@ -26,16 +26,20 @@ $(document).ready(function(e){
|
|||
return href
|
||||
}
|
||||
}
|
||||
function loadFramesForVideosInView(){
|
||||
videosTableDrawArea.find('.video-thumbnail').each(async (n,imgEl) => {
|
||||
const el = $(imgEl)
|
||||
const monitorId = el.attr('data-mid')
|
||||
const startDate = el.attr('data-time')
|
||||
const endDate = el.attr('data-end')
|
||||
const imgBlock = el.find('.video-thumbnail-img-block')
|
||||
const href = await getSnapshotFromVideoTimeFrame(monitorId,startDate,endDate)
|
||||
imgBlock.find('img').attr('src',href)
|
||||
})
|
||||
//Lazy Load Thumbnails
|
||||
function loadFramesForVideosInView() {
|
||||
videosTableDrawArea.find('.video-thumbnail').each(async (n, imgEl) => {
|
||||
const el = $(imgEl);
|
||||
const monitorId = el.attr('data-mid');
|
||||
const startDate = el.attr('data-time');
|
||||
const endDate = el.attr('data-end');
|
||||
const imgBlock = el.find('.video-thumbnail-img-block');
|
||||
|
||||
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){
|
||||
drawMonitorListToSelector(monitorsList,null,null,true)
|
||||
|
@ -45,143 +49,169 @@ $(document).ready(function(e){
|
|||
}
|
||||
loadDateRangePicker(dateSelector,{
|
||||
onChange: function(start, end, label) {
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements()
|
||||
}
|
||||
})
|
||||
monitorsList.change(function(){
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
objectTagSearchField.change(function(){
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
cloudVideoCheckSwitch.change(function(){
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
async function drawVideosTableViewElements(usePreloadedData){
|
||||
var dateRange = getSelectedTime(dateSelector)
|
||||
var searchQuery = objectTagSearchField.val() || null
|
||||
var startDate = dateRange.startDate
|
||||
var endDate = dateRange.endDate
|
||||
var monitorId = monitorsList.val()
|
||||
var wantsArchivedVideo = getVideoSetSelected() === 'archive'
|
||||
var wantCloudVideo = wantCloudVideos()
|
||||
var frameIconsHtml = ''
|
||||
if(!usePreloadedData){
|
||||
loadedVideosTable = (await getVideos({
|
||||
monitorId,
|
||||
startDate,
|
||||
endDate,
|
||||
searchQuery,
|
||||
archived: wantsArchivedVideo,
|
||||
customVideoSet: wantCloudVideo ? 'cloudVideos' : null,
|
||||
})).videos;
|
||||
$.each(loadedVideosTable,function(n,v){
|
||||
loadedVideosInMemory[`${monitorId}${v.time}${v.type}`]
|
||||
})
|
||||
}
|
||||
// for (let i = 0; i < loadedVideosTable.length; i++) {
|
||||
// const file = loadedVideosTable[i]
|
||||
// const frameUrl = await getSnapshotFromVideoTimeFrame(file.mid,file.time,file.end);
|
||||
// file.frameUrl = frameUrl
|
||||
// }
|
||||
videosTableDrawArea.bootstrapTable('destroy')
|
||||
videosTableDrawArea.bootstrapTable({
|
||||
onPostBody: loadFramesForVideosInView,
|
||||
onPageChange: () => {
|
||||
setTimeout(() => {
|
||||
loadFramesForVideosInView()
|
||||
},500)
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function(...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
monitorsList.change(debounce(function(){
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements();
|
||||
}, 300));
|
||||
|
||||
objectTagSearchField.change(debounce(function(){
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements();
|
||||
}, 300));
|
||||
|
||||
cloudVideoCheckSwitch.change(debounce(function(){
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements();
|
||||
}, 300));
|
||||
|
||||
// Event listener for the Refresh link styled as a button
|
||||
$('.refresh-data').click(function(e) {
|
||||
e.preventDefault();
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements();
|
||||
});
|
||||
|
||||
async function drawVideosTableViewElements(pageNumber, pageSize, usePreloadedData) {
|
||||
// Set default values if pageNumber and pageSize are not provided
|
||||
pageNumber = pageNumber || 1;
|
||||
pageSize = pageSize || 10;
|
||||
|
||||
var dateRange = getSelectedTime(dateSelector);
|
||||
var searchQuery = objectTagSearchField.val() || null;
|
||||
var startDate = dateRange.startDate;
|
||||
var endDate = dateRange.endDate;
|
||||
var monitorId = monitorsList.val();
|
||||
var wantsArchivedVideo = getVideoSetSelected() === 'archive';
|
||||
var wantCloudVideo = wantCloudVideos();
|
||||
|
||||
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,
|
||||
pageList: [10, 25, 50, 100, 1000, 2000],
|
||||
columns: [
|
||||
{
|
||||
field: 'mid',
|
||||
title: '',
|
||||
checkbox: true,
|
||||
formatter: () => {
|
||||
return {
|
||||
checked: false
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'image',
|
||||
title: '',
|
||||
},
|
||||
{
|
||||
field: 'Monitor',
|
||||
title: '',
|
||||
},
|
||||
{
|
||||
field: 'time',
|
||||
title: lang['Time'],
|
||||
},
|
||||
{
|
||||
field: 'objects',
|
||||
title: lang['Objects Found']
|
||||
},
|
||||
{
|
||||
field: 'tags',
|
||||
title: ''
|
||||
},
|
||||
{
|
||||
field: 'size',
|
||||
title: ''
|
||||
},
|
||||
{
|
||||
field: 'buttons',
|
||||
title: ''
|
||||
}
|
||||
],
|
||||
data: loadedVideosTable.map((file) => {
|
||||
var isLocalVideo = !wantCloudVideo
|
||||
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
|
||||
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}">
|
||||
<div class="video-thumbnail-img-block">
|
||||
<img class="pop-image cursor-pointer" style="min-width: 100px;min-height: 75px;">
|
||||
</div>
|
||||
<div class="video-thumbnail-buttons d-flex">
|
||||
<a class="video-thumbnail-button-cell open-snapshot p-3">
|
||||
<i class="fa fa-camera"></i>
|
||||
</a>
|
||||
<a class="video-thumbnail-button-cell preview-video p-3" href="${href}" title="${lang.Play}">
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>`,
|
||||
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
|
||||
mid: file.mid,
|
||||
time: `
|
||||
<div>${timeAgo(file.time)}</div>
|
||||
<div><small><b>${lang.Start} :</b> ${formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>
|
||||
<div><small><b>${lang.End} :</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>`,
|
||||
objects: file.objects,
|
||||
tags: `
|
||||
${file.ext ? `<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>` : ''}
|
||||
${!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>
|
||||
{
|
||||
field: 'image',
|
||||
title: '',
|
||||
},
|
||||
{
|
||||
field: 'Monitor',
|
||||
title: '',
|
||||
},
|
||||
{
|
||||
field: 'time',
|
||||
title: lang['Time'],
|
||||
},
|
||||
{
|
||||
field: 'objects',
|
||||
title: lang['Objects Found']
|
||||
},
|
||||
{
|
||||
field: 'tags',
|
||||
title: ''
|
||||
},
|
||||
{
|
||||
field: 'size',
|
||||
title: ''
|
||||
},
|
||||
{
|
||||
field: 'buttons',
|
||||
title: ''
|
||||
}
|
||||
],
|
||||
data: loadedVideosTable.map((file) => {
|
||||
var isLocalVideo = !wantCloudVideo;
|
||||
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`;
|
||||
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}">
|
||||
<div class="video-thumbnail-img-block">
|
||||
<img class="pop-image cursor-pointer" style="min-width: 100px;min-height: 75px;">
|
||||
</div>
|
||||
<div class="video-thumbnail-buttons d-flex">
|
||||
<a class="video-thumbnail-button-cell open-snapshot p-3">
|
||||
<i class="fa fa-camera"></i>
|
||||
</a>
|
||||
<a class="video-thumbnail-button-cell preview-video p-3" href="${href}" title="${lang.Play}">
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>`,
|
||||
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
|
||||
mid: file.mid,
|
||||
time: `
|
||||
<div>${timeAgo(file.time)}</div>
|
||||
<div><small><b>${lang.Start} :</b> ${formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>
|
||||
<div><small><b>${lang.End} :</b> ${formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA')}</small></div>`,
|
||||
objects: file.objects,
|
||||
tags: `
|
||||
${file.ext ? `<span class="badge badge-${file.ext ==='webm' ? `primary` : 'danger'}">${file.ext}</span>` : ''}
|
||||
${!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>
|
||||
`,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
function drawPreviewVideo(href){
|
||||
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}`])
|
||||
clearTimeout(redrawTimeout)
|
||||
redrawTimeout = setTimeout(function(){
|
||||
videosTableDrawArea.bootstrapTable('destroy');
|
||||
drawVideosTableViewElements(true)
|
||||
},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 -->
|
||||
<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/pnotify.custom.min.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/socket.io.min.js"></script>
|
||||
|
|
Loading…
Reference in New Issue