Bug Fix Bandit

alpha
Moe 2024-08-22 18:29:10 +00:00
parent 4979b1d57e
commit f41fe9ecf6
32 changed files with 781 additions and 501 deletions

View File

@ -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',

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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": "ストリームフラグ",

View File

@ -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

106
libs/checker/utils.js Normal file
View File

@ -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,
}
}

View File

@ -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){

View File

@ -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)

View File

@ -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)

View File

@ -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,
} }
} }

View File

@ -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
} }

View File

@ -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`)

View File

@ -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}`)

View File

@ -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) => {

View File

@ -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

View File

@ -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({

View File

@ -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",

View File

@ -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;

View File

@ -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){

View File

@ -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)
})

View File

@ -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;
}) })

View File

@ -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)
}) // })

View File

@ -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

View File

@ -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]);
} }

View File

@ -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,

View File

@ -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')
}) })

View File

@ -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;

View File

@ -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)
} }

View File

@ -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;
}
});
})();

View File

@ -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>