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
Moe 2022-07-05 21:59:37 -07:00
parent a4c5a0d58a
commit 450b7ab855
9 changed files with 116 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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