Option to Search for videos by Object Tag
this feature will only work on detections and recordings made after this update is applied. Previous detections are not searchable in this way.timelapse-frames-through-websocket
parent
a4c5a0d58a
commit
450b7ab855
|
@ -8000,6 +8000,11 @@ module.exports = function(s,config,lang){
|
|||
"class": "monitors_list",
|
||||
"possible": []
|
||||
},
|
||||
{
|
||||
"id": "videosTable_tag_search",
|
||||
"field": lang["Search Object Tags"],
|
||||
"example": "person",
|
||||
},
|
||||
{
|
||||
"class": "date_selector",
|
||||
"field": lang.Date,
|
||||
|
|
|
@ -1180,6 +1180,7 @@
|
|||
"Loop Stream": "Loop Stream",
|
||||
"Object Count": "Object Count",
|
||||
"Object Tag": "Object Tag",
|
||||
"Search Object Tags": "Search Object Tags",
|
||||
"Noise Filter": "Noise Filter",
|
||||
"Noise Filter Range": "Noise Filter Range",
|
||||
"TV Channel": "TV Channel",
|
||||
|
|
14
libs/sql.js
14
libs/sql.js
|
@ -162,6 +162,20 @@ module.exports = function(s,config){
|
|||
}catch(err){
|
||||
console.log(err)
|
||||
}
|
||||
try{
|
||||
s.databaseEngine.schema.table('Videos', table => {
|
||||
table.string('objects')
|
||||
}).then(() => {
|
||||
console.log(`objects added to Videos table`)
|
||||
}).catch((err) => {
|
||||
if(err && err.code !== 'ER_DUP_FIELDNAME'){
|
||||
console.log('error')
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
}
|
||||
delete(s.preQueries)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,9 +210,34 @@ module.exports = (s,config,lang) => {
|
|||
})
|
||||
})
|
||||
}
|
||||
async function getVideosBasedOnTagFoundInMatrixOfAssociatedEvent({
|
||||
groupKey,
|
||||
monitorId,
|
||||
startTime,
|
||||
endTime,
|
||||
searchQuery,
|
||||
monitorRestrictions
|
||||
}){
|
||||
const initialEventQuery = [
|
||||
['ke','=',groupKey],
|
||||
['objects','LIKE',`%${searchQuery}%`],
|
||||
]
|
||||
if(monitorId)initialEventQuery.push(['mid','=',monitorId]);
|
||||
if(startTime)initialEventQuery.push(['time','>',startTime]);
|
||||
if(endTime)initialEventQuery.push(['end','<',endTime]);
|
||||
if(monitorRestrictions)initialEventQuery.push(monitorRestrictions);
|
||||
const videoSelectResponse = await s.knexQueryPromise({
|
||||
action: "select",
|
||||
columns: "*",
|
||||
table: "Videos",
|
||||
where: initialEventQuery
|
||||
});
|
||||
return videoSelectResponse
|
||||
}
|
||||
return {
|
||||
orphanedVideoCheck: orphanedVideoCheck,
|
||||
scanForOrphanedVideos: scanForOrphanedVideos,
|
||||
cutVideoLength: cutVideoLength,
|
||||
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent: getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ module.exports = function(s,config,lang){
|
|||
ext: e.ext,
|
||||
status: 1,
|
||||
details: s.s(k.details),
|
||||
objects: k.objects || '',
|
||||
size: k.filesize,
|
||||
end: k.endTime,
|
||||
}
|
||||
|
@ -116,6 +117,9 @@ module.exports = function(s,config,lang){
|
|||
if(k.fileExists===true){
|
||||
//close video row
|
||||
k.details = k.details && k.details instanceof Object ? k.details : {}
|
||||
var listOEvents = activeMonitor.detector_motion_count || []
|
||||
var listOTags = listOEvents.filter(row => row.details.reason === 'object').map(row => row.details.matrices.map(matrix => matrix.tag).join(',')).join(',').split(',')
|
||||
if(listOTags)k.objects = [...new Set(listOTags)].join(',');
|
||||
k.stat = fs.statSync(k.dir+k.file)
|
||||
k.filesize = k.stat.size
|
||||
k.filesizeMB = parseFloat((k.filesize/1048576).toFixed(2))
|
||||
|
@ -137,6 +141,7 @@ module.exports = function(s,config,lang){
|
|||
ke: e.ke,
|
||||
filename: k.filename,
|
||||
filesize: k.filesize,
|
||||
objects: k.objects,
|
||||
time: s.timeObject(k.startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
end: s.timeObject(k.endTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
@ -156,6 +161,7 @@ module.exports = function(s,config,lang){
|
|||
time: k.startTime,
|
||||
size: k.filesize,
|
||||
end: k.endTime,
|
||||
objects: k.objects,
|
||||
events: monitorEventsCounted && monitorEventsCounted.length > 0 ? monitorEventsCounted : null
|
||||
},'GRP_'+e.ke,'video_view')
|
||||
//purge over max
|
||||
|
|
|
@ -30,6 +30,9 @@ module.exports = function(s,config,lang,app,io){
|
|||
spawnSubstreamProcess,
|
||||
destroySubstreamProcess,
|
||||
} = require('./monitor/utils.js')(s,config,lang)
|
||||
const {
|
||||
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
|
||||
} = require('./video/utils.js')(s,config,lang)
|
||||
s.renderPage = function(req,res,paths,passables,callback){
|
||||
passables.window = {}
|
||||
passables.data = req.params
|
||||
|
@ -970,6 +973,43 @@ module.exports = function(s,config,lang,app,io){
|
|||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Videos
|
||||
*/
|
||||
app.get([
|
||||
config.webPaths.apiPrefix+':auth/videosByEventTag/:ke',
|
||||
config.webPaths.apiPrefix+':auth/videosByEventTag/:ke/:id'
|
||||
], function (req,res){
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
s.auth(req.params,function(user){
|
||||
const searchQuery = s.getPostData(req,'search')
|
||||
const startTime = s.getPostData(req,'start')
|
||||
const endTime = s.getPostData(req,'end')
|
||||
const userDetails = user.details
|
||||
const monitorId = req.params.id
|
||||
const groupKey = req.params.ke
|
||||
const monitorRestrictions = s.getMonitorRestrictions(user.details,monitorId)
|
||||
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent({
|
||||
groupKey,
|
||||
monitorId,
|
||||
startTime,
|
||||
endTime,
|
||||
searchQuery,
|
||||
monitorRestrictions,
|
||||
}).then((response) => {
|
||||
if(response && response.rows){
|
||||
s.buildVideoLinks(response.rows,{
|
||||
auth : req.params.auth,
|
||||
videoParam : 'videos',
|
||||
})
|
||||
}
|
||||
s.closeJsonResponse(res,{
|
||||
ok: true,
|
||||
videos: response.rows,
|
||||
})
|
||||
})
|
||||
},res,req);
|
||||
});
|
||||
/**
|
||||
* API : Get Events
|
||||
*/
|
||||
app.get([
|
||||
|
|
|
@ -679,7 +679,7 @@ function permissionCheck(toCheck,monitorId){
|
|||
return false
|
||||
}
|
||||
|
||||
function drawMonitorListToSelector(jqTarget,selectFirst,showId){
|
||||
function drawMonitorListToSelector(jqTarget,selectFirst,showId,addAllMonitorsOption){
|
||||
var html = ''
|
||||
$.each(loadedMonitors,function(n,v){
|
||||
html += createOptionHtml({
|
||||
|
@ -687,7 +687,10 @@ function drawMonitorListToSelector(jqTarget,selectFirst,showId){
|
|||
label: v.name + (showId ? ` (${v.mid})` : ''),
|
||||
})
|
||||
})
|
||||
jqTarget.html(html)
|
||||
addAllMonitorsOption ? jqTarget.html(`
|
||||
<option value="">${lang['All Monitors']}</option>
|
||||
<optgroup label="${lang.Monitors}">${html}</optgroup>
|
||||
`) : jqTarget.html(html);
|
||||
if(selectFirst){
|
||||
jqTarget
|
||||
.find('option')
|
||||
|
|
|
@ -400,6 +400,7 @@ function loadVideoData(video){
|
|||
function getVideos(options,callback){
|
||||
return new Promise((resolve,reject) => {
|
||||
options = options ? options : {}
|
||||
var searchQuery = options.searchQuery
|
||||
var requestQueries = []
|
||||
var monitorId = options.monitorId
|
||||
var limit = options.limit || 300
|
||||
|
@ -415,7 +416,10 @@ function getVideos(options,callback){
|
|||
eventEndTime = formattedTimeForFilename(options.endDate,false)
|
||||
requestQueries.push(`end=${eventEndTime}`)
|
||||
}
|
||||
$.getJSON(`${getApiPrefix(`videos`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(data){
|
||||
if(searchQuery){
|
||||
requestQueries.push(`search=${searchQuery}`)
|
||||
}
|
||||
$.getJSON(`${getApiPrefix(searchQuery ? `videosByEventTag` : `videos`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(data){
|
||||
var videos = data.videos
|
||||
$.getJSON(`${getApiPrefix(`timelapse`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(timelapseFrames){
|
||||
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){
|
||||
|
|
|
@ -4,6 +4,7 @@ $(document).ready(function(e){
|
|||
var dateSelector = theEnclosure.find('.date_selector')
|
||||
var videosTableDrawArea = $('#videosTable_draw_area')
|
||||
var videosTablePreviewArea = $('#videosTable_preview_area')
|
||||
var objectTagSearchField = $('#videosTable_tag_search')
|
||||
var loadedVideosTable = [];
|
||||
var redrawTimeout;
|
||||
function openVideosTableView(monitorId,startDate,endDate){
|
||||
|
@ -38,19 +39,22 @@ $(document).ready(function(e){
|
|||
monitorsList.change(function(){
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
objectTagSearchField.change(function(){
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
async function drawVideosTableViewElements(usePreloadedData){
|
||||
var dateRange = getSelectedTime(false)
|
||||
var searchQuery = objectTagSearchField.val() || null
|
||||
var startDate = dateRange.startDate
|
||||
var endDate = dateRange.endDate
|
||||
var monitorId = monitorsList.val()
|
||||
var queryString = ['start=' + startDate,'end=' + endDate,'limit=0']
|
||||
var frameIconsHtml = ''
|
||||
var apiURL = getApiPrefix('videos') + '/' + monitorId;
|
||||
if(!usePreloadedData){
|
||||
loadedVideosTable = (await getVideos({
|
||||
monitorId,
|
||||
startDate,
|
||||
endDate,
|
||||
searchQuery,
|
||||
})).videos;
|
||||
$.each(loadedVideosTable,function(n,v){
|
||||
loadedVideosInMemory[`${monitorId}${v.time}`]
|
||||
|
@ -71,6 +75,10 @@ $(document).ready(function(e){
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'Monitor',
|
||||
title: '',
|
||||
},
|
||||
{
|
||||
field: 'time',
|
||||
title: lang['Time Created'],
|
||||
|
@ -90,7 +98,9 @@ $(document).ready(function(e){
|
|||
],
|
||||
data: loadedVideosTable.map((file) => {
|
||||
var href = getFullOrigin(true) + file.href
|
||||
var loadedMonitor = loadedMonitors[file.mid]
|
||||
return {
|
||||
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
|
||||
mid: file.mid,
|
||||
time: formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA'),
|
||||
end: formattedTime(file.end, 'DD-MM-YYYY hh:mm:ss AA'),
|
||||
|
@ -130,7 +140,7 @@ $(document).ready(function(e){
|
|||
e.preventDefault()
|
||||
var monitorId = getRowsMonitorId(this)
|
||||
openTab(`videosTableView`,{},null,null,null,() => {
|
||||
drawMonitorListToSelector(monitorsList)
|
||||
drawMonitorListToSelector(monitorsList,null,null,true)
|
||||
monitorsList.val(monitorId)
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
|
@ -197,12 +207,12 @@ $(document).ready(function(e){
|
|||
}
|
||||
})
|
||||
addOnTabOpen('videosTableView', function () {
|
||||
drawMonitorListToSelector(monitorsList)
|
||||
drawMonitorListToSelector(monitorsList,null,null,true)
|
||||
drawVideosTableViewElements()
|
||||
})
|
||||
addOnTabReopen('videosTableView', function () {
|
||||
var theSelected = `${monitorsList.val()}`
|
||||
drawMonitorListToSelector(monitorsList)
|
||||
drawMonitorListToSelector(monitorsList,null,null,true)
|
||||
monitorsList.val(theSelected)
|
||||
})
|
||||
addOnTabAway('videosTableView', function () {
|
||||
|
|
Loading…
Reference in New Issue