Merge branch 'master' of github.com:ZoneMinder/zoneminder

This commit is contained in:
Isaac Connor 2021-11-09 14:00:25 -05:00
commit 96a58f8c88
9 changed files with 83 additions and 74 deletions

View File

@ -4,7 +4,7 @@
web/api/lib
web/includes/csrf/
web/js/videojs.zoomrotate.js
web/skins/classic/js/bootstrap.js
web/skins/classic/js/bootstrap-4.5.0.js
web/skins/classic/js/chosen
web/skins/classic/js/dateTimePicker
web/skins/classic/js/jquery-*.js

View File

@ -323,32 +323,32 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
bool update = false;
//Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() );
if ( newNoteSetMap.size() > 0 ) {
if ( noteSetMap.size() == 0 ) {
if (newNoteSetMap.size() > 0) {
if (noteSetMap.size() == 0) {
noteSetMap = newNoteSetMap;
update = true;
} else {
for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin();
for (StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin();
newNoteSetMapIter != newNoteSetMap.end();
++newNoteSetMapIter ) {
++newNoteSetMapIter) {
const std::string &newNoteGroup = newNoteSetMapIter->first;
const StringSet &newNoteSet = newNoteSetMapIter->second;
//Info( "Got %d new strings", newNoteSet.size() );
if ( newNoteSet.size() > 0 ) {
if (newNoteSet.size() > 0) {
StringSetMap::iterator noteSetMapIter = noteSetMap.find(newNoteGroup);
if ( noteSetMapIter == noteSetMap.end() ) {
//Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() );
if (noteSetMapIter == noteSetMap.end()) {
//Debug(3, "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size());
noteSetMap.insert(StringSetMap::value_type(newNoteGroup, newNoteSet));
update = true;
} else {
StringSet &noteSet = noteSetMapIter->second;
//Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() );
for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin();
//Debug(3, "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size());
for (StringSet::const_iterator newNoteSetIter = newNoteSet.begin();
newNoteSetIter != newNoteSet.end();
++newNoteSetIter ) {
++newNoteSetIter) {
const std::string &newNote = *newNoteSetIter;
StringSet::iterator noteSetIter = noteSet.find(newNote);
if ( noteSetIter == noteSet.end() ) {
if (noteSetIter == noteSet.end()) {
noteSet.insert(newNote);
update = true;
}

View File

@ -141,7 +141,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]);
event_data->start_time = SystemTimePoint(Seconds(atoi(dbrow[3])));
event_data->end_time = dbrow[4] ? SystemTimePoint(Seconds(atoi(dbrow[4]))) : SystemTimePoint();
event_data->end_time = dbrow[4] ? SystemTimePoint(Seconds(atoi(dbrow[4]))) : std::chrono::system_clock::now();
event_data->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
event_data->frames_duration =
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));

View File

@ -1862,8 +1862,6 @@ bool Monitor::Analyse() {
Debug(3, "signal and active and modect");
Event::StringSet zoneSet;
int motion_score = last_motion_score;
if (analysis_fps_limit) {
double capture_fps = get_capture_fps();
motion_frame_skip = capture_fps / analysis_fps_limit;
@ -1880,7 +1878,7 @@ bool Monitor::Analyse() {
} else {
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// Get new score.
motion_score = DetectMotion(*(snap->image), zoneSet);
int motion_score = DetectMotion(*(snap->image), zoneSet);
snap->zone_stats.reserve(zones.size());
for (const Zone &zone : zones) {
@ -1892,21 +1890,20 @@ bool Monitor::Analyse() {
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score);
motion_frame_count += 1;
// Why are we updating the last_motion_score too?
last_motion_score = motion_score;
if (motion_score) {
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
}
} else {
Debug(1, "no image so skipping motion detection");
} // end if has image
} else {
Debug(1, "Skipped motion detection last motion score was %d", motion_score);
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
}
if (motion_score) {
score += motion_score;
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
score += last_motion_score;
} else {
Debug(1, "Not Active(%d) enabled %d active %d doing motion detection: %d",
Active(), enabled, shared_data->active,
@ -2007,7 +2004,7 @@ bool Monitor::Analyse() {
} // end if ! event
} // end if RECORDING
if (score and (function == MODECT or function == NODECT)) {
if (score and (function != MONITOR)) {
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
// If we should end then previous continuous event and start a new non-continuous event
if (event && event->Frames()
@ -2142,7 +2139,8 @@ bool Monitor::Analyse() {
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
} else {
Debug(1,
"State %s because image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
"State %d %s because analysis_image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
state,
State_Strings[state].c_str(),
analysis_image_count,
last_alarm_count,
@ -2161,12 +2159,10 @@ bool Monitor::Analyse() {
// Generate analysis images if necessary
if ((savejpegs > 1) and snap->image) {
for (const Zone &zone : zones) {
if (zone.Alarmed()) {
if (zone.AlarmImage()) {
if (zone.Alarmed() and zone.AlarmImage()) {
if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zone.AlarmImage()));
}
} // end if zone is alarmed
} // end foreach zone
} // end if savejpegs

View File

@ -67,20 +67,19 @@ if (isset($_REQUEST['sort'])) {
// Offset specifies the starting row to return, used for pagination
$offset = 0;
if ( isset($_REQUEST['offset']) ) {
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
if (isset($_REQUEST['offset'])) {
if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
} else {
$offset = $_REQUEST['offset'];
}
}
// Limit specifies the number of rows to return
// Set the default to 0 for events view, to prevent an issue with ALL pagination
$limit = 0;
if ( isset($_REQUEST['limit']) ) {
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
if (isset($_REQUEST['limit'])) {
if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
} else {
$limit = $_REQUEST['limit'];
@ -91,25 +90,24 @@ if ( isset($_REQUEST['limit']) ) {
// MAIN LOOP
//
switch ( $task ) {
switch ($task) {
case 'archive' :
foreach ( $eids as $eid ) archiveRequest($task, $eid);
foreach ($eids as $eid) archiveRequest($task, $eid);
break;
case 'unarchive' :
# The idea is that anyone can archive, but only people with Event Edit permission can unarchive..
if ( !canEdit('Events') ) {
if (!canEdit('Events')) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) archiveRequest($task, $eid);
foreach ($eids as $eid) archiveRequest($task, $eid);
break;
case 'delete' :
if ( !canEdit('Events') ) {
if (!canEdit('Events')) {
ajaxError('Insufficient permissions for user '.$user['Username']);
return;
}
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
foreach ($eids as $eid) $data[] = deleteRequest($eid);
break;
case 'query' :
$data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit);
@ -139,6 +137,8 @@ function deleteRequest($eid) {
$message[] = array($eid=>'Event not found.');
} else if ( $event->Archived() ) {
$message[] = array($eid=>'Event is archived, cannot delete it.');
} else if (!$event->canEdit()) {
$message[] = array($eid=>'You do not have permission to delete event '.$event->Id());
} else {
$event->delete();
}
@ -147,7 +147,6 @@ function deleteRequest($eid) {
}
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
$data = array(
'total' => 0,
'totalNotFiltered' => 0,
@ -156,7 +155,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
);
$failed = !$filter->test_pre_sql_conditions();
if ( $failed ) {
if ($failed) {
ZM\Debug('Pre conditions failed, not doing sql');
return $data;
}
@ -171,7 +170,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
// The names of columns shown in the event view that are NOT dB columns in the database
$col_alt = array('Monitor', 'Storage');
if ( !in_array($sort, array_merge($columns, $col_alt)) ) {
if (!in_array($sort, array_merge($columns, $col_alt))) {
ZM\Error('Invalid sort field: ' . $sort);
$sort = 'Id';
}
@ -186,7 +185,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$storage_areas = ZM\Storage::find();
$StorageById = array();
foreach ( $storage_areas as $S ) {
foreach ($storage_areas as $S) {
$StorageById[$S->Id()] = $S;
}
@ -195,41 +194,43 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
ZM\Debug('Calling the following sql query: ' .$sql);
$query = dbQuery($sql, $values);
if ( $query ) {
while ( $row = dbFetchNext($query) ) {
$event = new ZM\Event($row);
$event->remove_from_cache();
if ( !$filter->test_post_sql_conditions($event) ) {
continue;
}
$event_ids[] = $event->Id();
$unfiltered_rows[] = $row;
} # end foreach row
if (!$query) {
ajaxError(dbError($sql));
return;
}
while ($row = dbFetchNext($query)) {
$event = new ZM\Event($row);
$event->remove_from_cache();
if (!$filter->test_post_sql_conditions($event)) {
continue;
}
$event_ids[] = $event->Id();
$unfiltered_rows[] = $row;
} # end foreach row
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
$filtered_rows = null;
if ( count($advsearch) or $search != '' ) {
if (count($advsearch) or $search != '') {
$search_filter = new ZM\Filter();
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids));
// There are two search bars in the log view, normal and advanced
// Making an exuctive decision to ignore the normal search, when advanced search is in use
// Alternatively we could try to do both
if ( count($advsearch) ) {
if (count($advsearch)) {
$terms = array();
foreach ( $advsearch as $col=>$text ) {
foreach ($advsearch as $col=>$text) {
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
} # end foreach col in advsearch
$terms[0]['obr'] = 1;
$terms[count($terms)-1]['cbr'] = 1;
$search_filter->addTerms($terms);
} else if ( $search != '' ) {
} else if ($search != '') {
$search = '%' .$search. '%';
$terms = array();
foreach ( $columns as $col ) {
foreach ($columns as $col) {
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
}
$terms[0]['obr'] = 1;

View File

@ -38,7 +38,7 @@ if ($zmuOutput) {
$ctls = shell_exec('v4l2-ctl -d '.$monitor->Device().' --list-ctrls');
if (!$ctls) {
ZM\Warning("Guessing v4l ctrls. We need v4l2-ctl please install it");
ZM\Warning('Guessing v4l ctrls. We need v4l2-ctl please install it');
$ctls = '
brightness 0x00980900 (int) : min=-10 max=10 step=1 default=0 value=8
contrast 0x00980901 (int) : min=0 max=20 step=1 default=10 value=12
@ -83,10 +83,15 @@ foreach ($ctls as $line) {
}
}
$label = translate($setting_uc);
if ($label == $setting_uc) {
$label = ucwords(str_replace('_', ' ', $label));
}
if ($setting == 'brightness' or $setting == 'colour' or $setting == 'contrast' or $setting == 'hue') {
echo '
<tr>
<th scope="row">'.translate($setting_uc).'</th>
<th scope="row">'.$label.'</th>
<td>'.$min.'</td><td><input type="range" title="'.$value.'" min="'.$min.'" max="'.$max.'" step="'.$step.'" default="'.$default.'" value="'.$value.'" id="new'.$setting_uc.'" name="new'.$setting_uc.'" '.(canEdit('Control') ? '' : 'disabled="disabled"') .'/></td><td>'.$max.'</td>
</tr>
';
@ -94,7 +99,7 @@ foreach ($ctls as $line) {
if ($type == '(bool)') {
echo '
<tr>
<th scope="row">'.translate($setting_uc).'</th>
<th scope="row">'.$label.'</th>
<td></td><td>'.html_radio('new'.$setting_uc, array('0'=>translate('True'), '1', translate('False')), $value, array('disabled'=>'disabled')).'
</td><td></td>
</tr>
@ -102,14 +107,14 @@ foreach ($ctls as $line) {
} else if ($type == '(int)') {
echo '
<tr>
<th scope="row">'.translate($setting_uc).'</th>
<th scope="row">'.$label.'</th>
<td></td><td><input type="range" '.$ctl[1].' disabled="disabled"/></td><td></td>
</tr>
';
} else {
echo '
<tr>
<th scope="row">'.translate($setting_uc).'</th>
<th scope="row">'.$label.'</th>
<td></td><td>'.$value.'</td><td></td>
</tr>
';

View File

@ -112,7 +112,7 @@ function dbLog($sql, $update=false) {
function dbError($sql) {
global $dbConn;
$error = $dbConn->errorInfo();
if ( !$error[0] )
if (!$error[0])
return '';
$message = "SQL-ERR '".implode("\n", $dbConn->errorInfo())."', statement was '".$sql."'";
@ -130,17 +130,17 @@ function dbEscape( $string ) {
function dbQuery($sql, $params=NULL, $debug = false) {
global $dbConn;
if ( dbLog($sql, true) )
if (dbLog($sql, true))
return;
$result = NULL;
try {
if ( isset($params) ) {
if ( ! $result = $dbConn->prepare($sql) ) {
if (isset($params)) {
if (!$result = $dbConn->prepare($sql)) {
ZM\Error("SQL: Error preparing $sql: " . $pdo->errorInfo);
return NULL;
}
if ( ! $result->execute($params) ) {
if (!$result->execute($params)) {
ZM\Error("SQL: Error executing $sql: " . print_r($result->errorInfo(), true));
return NULL;
}

View File

@ -35,12 +35,16 @@ var params =
// Called by bootstrap-table to retrieve zm event data
function ajaxRequest(params) {
if ( params.data && params.data.filter ) {
if (params.data && params.data.filter) {
params.data.advsearch = params.data.filter;
delete params.data.filter;
}
$j.getJSON(thisUrl + '?view=request&request=events&task=query'+filterQuery, params.data)
.done(function(data) {
if (data.result == 'Error') {
alert(data.message);
return;
}
var rows = processRows(data.rows);
// rearrange the result into what bootstrap-table expects
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});

View File

@ -461,9 +461,12 @@ switch ( $name ) {
<tr class="Id">
<td class="text-right pr-3"><?php echo translate('Id') ?></td>
<td><input type="number" step="1" min="1" name="newMonitor[Id]" placeholder="leave blank for auto"/><br/>
10 Available Ids:
<?php echo implode(', ', array_slice($available_monitor_ids, 0, 10)); ?>
</td>
<?php
if (count($available_monitor_ids)) {
echo 'Some available ids: '.implode(', ', array_slice($available_monitor_ids, 0, 10));
}
?>
</td>
</tr>
<?php