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", "class": "monitors_list",
"possible": [] "possible": []
}, },
{
"id": "videosTable_tag_search",
"field": lang["Search Object Tags"],
"example": "person",
},
{ {
"class": "date_selector", "class": "date_selector",
"field": lang.Date, "field": lang.Date,

View File

@ -1180,6 +1180,7 @@
"Loop Stream": "Loop Stream", "Loop Stream": "Loop Stream",
"Object Count": "Object Count", "Object Count": "Object Count",
"Object Tag": "Object Tag", "Object Tag": "Object Tag",
"Search Object Tags": "Search Object Tags",
"Noise Filter": "Noise Filter", "Noise Filter": "Noise Filter",
"Noise Filter Range": "Noise Filter Range", "Noise Filter Range": "Noise Filter Range",
"TV Channel": "TV Channel", "TV Channel": "TV Channel",

View File

@ -162,6 +162,20 @@ module.exports = function(s,config){
}catch(err){ }catch(err){
console.log(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) 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 { return {
orphanedVideoCheck: orphanedVideoCheck, orphanedVideoCheck: orphanedVideoCheck,
scanForOrphanedVideos: scanForOrphanedVideos, scanForOrphanedVideos: scanForOrphanedVideos,
cutVideoLength: cutVideoLength, cutVideoLength: cutVideoLength,
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent: getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
} }
} }

View File

@ -80,6 +80,7 @@ module.exports = function(s,config,lang){
ext: e.ext, ext: e.ext,
status: 1, status: 1,
details: s.s(k.details), details: s.s(k.details),
objects: k.objects || '',
size: k.filesize, size: k.filesize,
end: k.endTime, end: k.endTime,
} }
@ -116,6 +117,9 @@ module.exports = function(s,config,lang){
if(k.fileExists===true){ if(k.fileExists===true){
//close video row //close video row
k.details = k.details && k.details instanceof Object ? k.details : {} 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.stat = fs.statSync(k.dir+k.file)
k.filesize = k.stat.size k.filesize = k.stat.size
k.filesizeMB = parseFloat((k.filesize/1048576).toFixed(2)) k.filesizeMB = parseFloat((k.filesize/1048576).toFixed(2))
@ -137,6 +141,7 @@ module.exports = function(s,config,lang){
ke: e.ke, ke: e.ke,
filename: k.filename, filename: k.filename,
filesize: k.filesize, filesize: k.filesize,
objects: k.objects,
time: s.timeObject(k.startTime).format('YYYY-MM-DD HH:mm:ss'), time: s.timeObject(k.startTime).format('YYYY-MM-DD HH:mm:ss'),
end: s.timeObject(k.endTime).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, time: k.startTime,
size: k.filesize, size: k.filesize,
end: k.endTime, end: k.endTime,
objects: k.objects,
events: monitorEventsCounted && monitorEventsCounted.length > 0 ? monitorEventsCounted : null events: monitorEventsCounted && monitorEventsCounted.length > 0 ? monitorEventsCounted : null
},'GRP_'+e.ke,'video_view') },'GRP_'+e.ke,'video_view')
//purge over max //purge over max

View File

@ -30,6 +30,9 @@ module.exports = function(s,config,lang,app,io){
spawnSubstreamProcess, spawnSubstreamProcess,
destroySubstreamProcess, destroySubstreamProcess,
} = require('./monitor/utils.js')(s,config,lang) } = require('./monitor/utils.js')(s,config,lang)
const {
getVideosBasedOnTagFoundInMatrixOfAssociatedEvent,
} = require('./video/utils.js')(s,config,lang)
s.renderPage = function(req,res,paths,passables,callback){ s.renderPage = function(req,res,paths,passables,callback){
passables.window = {} passables.window = {}
passables.data = req.params passables.data = req.params
@ -970,6 +973,43 @@ module.exports = function(s,config,lang,app,io){
},res,req); },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 * API : Get Events
*/ */
app.get([ app.get([

View File

@ -679,7 +679,7 @@ function permissionCheck(toCheck,monitorId){
return false return false
} }
function drawMonitorListToSelector(jqTarget,selectFirst,showId){ function drawMonitorListToSelector(jqTarget,selectFirst,showId,addAllMonitorsOption){
var html = '' var html = ''
$.each(loadedMonitors,function(n,v){ $.each(loadedMonitors,function(n,v){
html += createOptionHtml({ html += createOptionHtml({
@ -687,7 +687,10 @@ function drawMonitorListToSelector(jqTarget,selectFirst,showId){
label: v.name + (showId ? ` (${v.mid})` : ''), 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){ if(selectFirst){
jqTarget jqTarget
.find('option') .find('option')

View File

@ -400,6 +400,7 @@ function loadVideoData(video){
function getVideos(options,callback){ function getVideos(options,callback){
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
options = options ? options : {} options = options ? options : {}
var searchQuery = options.searchQuery
var requestQueries = [] var requestQueries = []
var monitorId = options.monitorId var monitorId = options.monitorId
var limit = options.limit || 300 var limit = options.limit || 300
@ -415,7 +416,10 @@ function getVideos(options,callback){
eventEndTime = formattedTimeForFilename(options.endDate,false) eventEndTime = formattedTimeForFilename(options.endDate,false)
requestQueries.push(`end=${eventEndTime}`) 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 var videos = data.videos
$.getJSON(`${getApiPrefix(`timelapse`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(timelapseFrames){ $.getJSON(`${getApiPrefix(`timelapse`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`noLimit=1`]).join('&')}`,function(timelapseFrames){
$.getJSON(`${getApiPrefix(`events`)}${monitorId ? `/${monitorId}` : ''}?${requestQueries.concat([`limit=${limit}`]).join('&')}`,function(eventData){ $.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 dateSelector = theEnclosure.find('.date_selector')
var videosTableDrawArea = $('#videosTable_draw_area') var videosTableDrawArea = $('#videosTable_draw_area')
var videosTablePreviewArea = $('#videosTable_preview_area') var videosTablePreviewArea = $('#videosTable_preview_area')
var objectTagSearchField = $('#videosTable_tag_search')
var loadedVideosTable = []; var loadedVideosTable = [];
var redrawTimeout; var redrawTimeout;
function openVideosTableView(monitorId,startDate,endDate){ function openVideosTableView(monitorId,startDate,endDate){
@ -38,19 +39,22 @@ $(document).ready(function(e){
monitorsList.change(function(){ monitorsList.change(function(){
drawVideosTableViewElements() drawVideosTableViewElements()
}) })
objectTagSearchField.change(function(){
drawVideosTableViewElements()
})
async function drawVideosTableViewElements(usePreloadedData){ async function drawVideosTableViewElements(usePreloadedData){
var dateRange = getSelectedTime(false) var dateRange = getSelectedTime(false)
var searchQuery = objectTagSearchField.val() || null
var startDate = dateRange.startDate var startDate = dateRange.startDate
var endDate = dateRange.endDate var endDate = dateRange.endDate
var monitorId = monitorsList.val() var monitorId = monitorsList.val()
var queryString = ['start=' + startDate,'end=' + endDate,'limit=0']
var frameIconsHtml = '' var frameIconsHtml = ''
var apiURL = getApiPrefix('videos') + '/' + monitorId;
if(!usePreloadedData){ if(!usePreloadedData){
loadedVideosTable = (await getVideos({ loadedVideosTable = (await getVideos({
monitorId, monitorId,
startDate, startDate,
endDate, endDate,
searchQuery,
})).videos; })).videos;
$.each(loadedVideosTable,function(n,v){ $.each(loadedVideosTable,function(n,v){
loadedVideosInMemory[`${monitorId}${v.time}`] loadedVideosInMemory[`${monitorId}${v.time}`]
@ -71,6 +75,10 @@ $(document).ready(function(e){
} }
}, },
}, },
{
field: 'Monitor',
title: '',
},
{ {
field: 'time', field: 'time',
title: lang['Time Created'], title: lang['Time Created'],
@ -90,7 +98,9 @@ $(document).ready(function(e){
], ],
data: loadedVideosTable.map((file) => { data: loadedVideosTable.map((file) => {
var href = getFullOrigin(true) + file.href var href = getFullOrigin(true) + file.href
var loadedMonitor = loadedMonitors[file.mid]
return { return {
Monitor: loadedMonitor && loadedMonitor.name ? loadedMonitor.name : file.mid,
mid: file.mid, mid: file.mid,
time: formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA'), time: formattedTime(file.time, 'DD-MM-YYYY hh:mm:ss AA'),
end: formattedTime(file.end, '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() e.preventDefault()
var monitorId = getRowsMonitorId(this) var monitorId = getRowsMonitorId(this)
openTab(`videosTableView`,{},null,null,null,() => { openTab(`videosTableView`,{},null,null,null,() => {
drawMonitorListToSelector(monitorsList) drawMonitorListToSelector(monitorsList,null,null,true)
monitorsList.val(monitorId) monitorsList.val(monitorId)
drawVideosTableViewElements() drawVideosTableViewElements()
}) })
@ -197,12 +207,12 @@ $(document).ready(function(e){
} }
}) })
addOnTabOpen('videosTableView', function () { addOnTabOpen('videosTableView', function () {
drawMonitorListToSelector(monitorsList) drawMonitorListToSelector(monitorsList,null,null,true)
drawVideosTableViewElements() drawVideosTableViewElements()
}) })
addOnTabReopen('videosTableView', function () { addOnTabReopen('videosTableView', function () {
var theSelected = `${monitorsList.val()}` var theSelected = `${monitorsList.val()}`
drawMonitorListToSelector(monitorsList) drawMonitorListToSelector(monitorsList,null,null,true)
monitorsList.val(theSelected) monitorsList.val(theSelected)
}) })
addOnTabAway('videosTableView', function () { addOnTabAway('videosTableView', function () {