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

This commit is contained in:
Isaac Connor 2021-12-14 09:29:03 -05:00
commit cfcc39defb
22 changed files with 642 additions and 287 deletions

View File

@ -283,6 +283,7 @@ CREATE TABLE `Filters` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`UserId` int(10) unsigned,
`ExecuteInterval` int(10) unsigned NOT NULL default '60',
`Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoUnarchive` tinyint(3) unsigned NOT NULL default '0',

View File

@ -56,7 +56,7 @@ EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitor_Status'
AND column_name = 'DayEvents'
AND column_name = 'DayEventDiskSpace'
) > 0,
"ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`",
"SELECT 'Column DayEventDiskSpace already removed from Monitor_Status'"

18
db/zm_update-1.37.6.sql Normal file
View File

@ -0,0 +1,18 @@
--
-- Update Filters table to have a ExecuteInterval Column
--
SELECT 'Checking for ExecuteInterval in Filters';
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Filters'
AND table_schema = DATABASE()
AND column_name = 'ExecuteInterval'
) > 0,
"SELECT 'Column ExecuteInterval already exists in Filters'",
"ALTER TABLE Filters ADD COLUMN `ExecuteInterval` int(10) unsigned NOT NULL default '60' AFTER `UserId`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -36,7 +36,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.37.5
Version: 1.37.6
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons

View File

@ -56,6 +56,7 @@ $primary_key = 'Id';
%fields = map { $_ => $_ } qw(
Id
Name
ExecuteInterval
Query_json
AutoArchive
AutoUnarchive
@ -106,7 +107,6 @@ sub Execute {
$sql =~ s/zmSystemLoad/$load/g;
}
$sql .= ' FOR UPDATE' if $$self{LockRows};
Debug("Filter::Execute SQL ($sql)");
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
@ -371,10 +371,7 @@ sub Sql {
if ( @auto_terms ) {
$sql .= ' AND ( '.join(' or ', @auto_terms).' )';
}
if ( !$filter_expr->{sort_field} ) {
$filter_expr->{sort_field} = 'StartDateTime';
$filter_expr->{sort_asc} = 0;
}
my $sort_column = '';
if ( $filter_expr->{sort_field} eq 'Id' ) {
$sort_column = 'E.Id';
@ -406,14 +403,23 @@ sub Sql {
$sort_column = 'E.MaxScore';
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
$sort_column = 'E.DiskSpace';
} else {
$sort_column = 'E.StartDateTime';
} elsif ( $filter_expr->{sort_field} ne '' ) {
$sort_column = 'E.'.$filter_expr->{sort_field};
}
my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC';
$sql .= ' ORDER BY '.$sort_column.' '.$sort_order;
if ( $filter_expr->{limit} ) {
if ( $sort_column ne '' ) {
$sql .= ' ORDER BY '.$sort_column.' '.($filter_expr->{sort_asc} ? 'ASC' : 'DESC');
}
if ($filter_expr->{limit}) {
$sql .= ' LIMIT 0,'.$filter_expr->{limit};
}
if ($$self{LockRows}) {
$sql .= ' FOR UPDATE OF E'
} else {
$sql .= ' FOR SHARE OF E'
}
if ($filter_expr->{skip_locked}) {
$sql .= ' SKIP LOCKED';
}
$self->{Sql} = $sql;
} # end if has Sql
return $self->{Sql};

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory.
our %EXPORT_TAGS = (
constants => [ qw(
STATE_UNKNOWN
STATE_IDLE
STATE_PREALARM
STATE_ALARM
@ -98,11 +99,12 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
use constant STATE_IDLE => 0;
use constant STATE_PREALARM => 1;
use constant STATE_ALARM => 2;
use constant STATE_ALERT => 3;
use constant STATE_TAPE => 4;
use constant STATE_UNKNOWN => 0;
use constant STATE_IDLE => 1;
use constant STATE_PREALARM => 2;
use constant STATE_ALARM => 3;
use constant STATE_ALERT => 4;
use constant STATE_TAPE => 5;
use constant ACTION_GET => 1;
use constant ACTION_SET => 2;

View File

@ -138,7 +138,6 @@ $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0;
if (!EVENT_PATH) {
@ -174,6 +173,7 @@ my @filters;
my $last_action = 0;
while (!$zm_terminate) {
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $now = time;
if (($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY}) {
Debug('Reloading filters');
@ -183,6 +183,15 @@ while (!$zm_terminate) {
foreach my $filter (@filters) {
last if $zm_terminate;
my $elapsed = ($now - $$filter{last_ran});
if ($$filter{last_ran} and ($elapsed < $$filter{ExecuteInterval})) {
my $filter_delay = $$filter{ExecuteInterval} - ($now - $$filter{last_ran});
$delay = $filter_delay if $filter_delay < $delay;
Debug("Setting delay to $delay because ExecuteInterval=$$filter{ExecuteInterval} and $elapsed have elapsed");
next;
}
if ($$filter{Concurrent} and !($filter_id or $filter_name)) {
my ( $proc ) = $0 =~ /(\S+)/;
my ( $id ) = $$filter{Id} =~ /(\d+)/;
@ -191,8 +200,9 @@ while (!$zm_terminate) {
system(qq`$proc --filter_id $id &`);
} else {
checkFilter($filter);
$$filter{last_ran} = $now;
}
}
} # end foreach filter
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
@ -362,11 +372,6 @@ sub checkFilter {
} # end if AutoCopy
if ( $filter->{UpdateDiskSpace} ) {
if ( $$filter{LockRows} ) {
$ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load();
}
my $old_diskspace = $$Event{DiskSpace};
my $new_diskspace = $Event->DiskSpace(undef);

View File

@ -166,13 +166,9 @@ while (!$zm_terminate) {
foreach my $connection ( values(%spawned_connections) ) {
if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from spawned connection '
.$connection->name()
.' ('
.$connection->fileno()
.')'
);
.$connection->name().' ('.$connection->fileno().')');
my $messages = $connection->getMessages();
if ( defined($messages) ) {
if (defined($messages)) {
foreach my $message ( @$messages ) {
handleMessage($connection, $message);
}
@ -199,34 +195,32 @@ while (!$zm_terminate) {
# Check polled connections
foreach my $connection ( @in_poll_connections ) {
my $messages = $connection->getMessages();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage($connection, $message);
}
if (defined($messages)) {
foreach my $message (@$messages) { handleMessage($connection, $message) };
}
}
# Check for alarms that might have happened
my @out_messages;
foreach my $monitor ( values %monitors ) {
if ($$monitor{Function} eq 'None') {
$monitor_reload_time = 0;
next;
}
if ( ! zmMemVerify($monitor) ) {
if (!zmMemVerify($monitor)) {
# Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it.
push @needsReload, $monitor;
next;
}
my ( $state, $last_event ) = zmMemRead( $monitor,
[
my ($state, $last_event) = zmMemRead($monitor, [
'shared_data:state',
'shared_data:last_event'
]
);
]);
#print( "$monitor->{Id}: S:$state, LE:$last_event" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
if ($state == STATE_ALARM or $state == STATE_ALERT) {
# In alarm state
if ( !defined($monitor->{LastEvent})
or ($last_event != $monitor->{LastEvent})

View File

@ -65,7 +65,7 @@ unsigned int Buffer::expand(unsigned int count) {
int Buffer::read_into(int sd, unsigned int bytes) {
// Make sure there is enough space
this->expand(bytes);
Debug(3, "Reading %u btes", bytes);
Debug(3, "Reading %u bytes", bytes);
int bytes_read = ::read(sd, mTail, bytes);
if (bytes_read > 0) {
mTail += bytes_read;

View File

@ -118,38 +118,31 @@ constexpr Rgb kRGBTransparent = 0x01000000;
/* Convert RGB colour value into BGR\ARGB\ABGR */
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
Rgb result;
switch(p_subpixorder) {
Rgb result = 0;
switch (p_subpixorder) {
case ZM_SUBPIX_ORDER_BGR:
case ZM_SUBPIX_ORDER_BGRA:
{
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
}
break;
case ZM_SUBPIX_ORDER_ARGB:
{
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
}
break;
case ZM_SUBPIX_ORDER_ABGR:
{
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
}
break;
/* Grayscale */
case ZM_SUBPIX_ORDER_NONE:
result = p_col & 0xff;
break;
default:
return p_col;
result = p_col;
break;
}

View File

@ -365,6 +365,7 @@ int main(int argc, char *argv[]) {
monitor->Id());
zmDbDo(sql);
}
monitors.clear();
Image::Deinitialise();
Debug(1, "terminating");

View File

@ -218,8 +218,8 @@ rm .gitignore
cd ../
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]"
if [ -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz exists, overwrite it? [Y/n]"
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
fi;

View File

@ -1 +1 @@
1.37.5
1.37.6

View File

@ -48,7 +48,7 @@ if (isset($_REQUEST['order'])) {
} else if (strtolower($_REQUEST['order']) == 'desc') {
$order = 'DESC';
} else {
Warning("Invalid value for order " . $_REQUEST['order']);
Warning('Invalid value for order ' . $_REQUEST['order']);
}
}
@ -170,18 +170,23 @@ 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 ( $sort != '' ) {
if (!in_array($sort, array_merge($columns, $col_alt))) {
ZM\Error('Invalid sort field: ' . $sort);
$sort = 'Id';
$sort = '';
} else if ( $sort == 'Monitor' ) {
$sort = 'M.Name';
} else {
$sort = 'E.'.$sort;
}
}
$values = array();
$likes = array();
$where = $filter->sql()?' WHERE ('.$filter->sql().')' : '';
$sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort;
$col_str = 'E.*, M.Name AS Monitor';
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY '.$sort.' '.$order;
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:'');
$storage_areas = ZM\Storage::find();
$StorageById = array();

View File

@ -9,13 +9,13 @@
</div>
<div class="modal-body">
<?php
//require_once('includes/Filter.php');
require_once('includes/Filter.php');
$fid = validInt($_REQUEST['fid']);
if ( !$fid ) {
if (!$fid) {
echo '<div class="error">No filter id specified.</div>';
} else {
$filter = new ZM\Filter($_REQUEST['fid']);
if ( ! $filter->Id() ) {
if (!$filter->Id()) {
echo '<div class="error">Filter not found for id '.$_REQUEST['fid'].'</div>';
}
}
@ -25,7 +25,16 @@
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML();
?>
<p><label>SQL</label><?php echo $filter->sql() ?></p>
<p><label>SQL</label>
<?php
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultScale<br/>FROM Monitors AS M INNER JOIN Events AS E ON (M.Id = E.MonitorId)<br/>WHERE<br/>';
$sql .= $filter->sql();
$sql .= $filter->sort_field() ? ' ORDER BY '.$filter->sort_field(). ' ' .($filter->sort_asc() ? 'ASC' : 'DESC') : '';
$sql .= $filter->limit() ? ' LIMIT '.$filter->limit() : '';
$sql .= $filter->skip_locked() ? ' SKIP LOCKED' : '';
echo $sql;
?></p>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel')?> </button>
</div>

View File

@ -321,12 +321,20 @@ class MonitorsController extends AppController {
}
$monitor = $this->Monitor->find('first', array(
'fields' => array('Id', 'Type', 'Device'),
'fields' => array('Id', 'Type', 'Device', 'Function'),
'conditions' => array('Id' => $id)
));
// Clean up the returned array
$monitor = Set::extract('/Monitor/.', $monitor);
if ($monitor[0]['Function'] == 'None') {
$this->set(array(
'status' => false,
'statustext' => 'Monitor function is set to None',
'_serialize' => array('status','statustext'),
));
return;
}
// Pass -d for local, otherwise -m
if ( $monitor[0]['Type'] == 'Local' ) {

View File

@ -9,6 +9,8 @@ class Filter extends ZM_Object {
protected $defaults = array(
'Id' => null,
'Name' => '',
'UserId' => 0,
'ExecuteInterval' => 60,
'AutoExecute' => 0,
'AutoExecuteCmd' => '',
'AutoEmail' => 0,
@ -26,7 +28,6 @@ class Filter extends ZM_Object {
'AutoCopy' => 0,
'AutoCopyTo' => 0,
'UpdateDiskSpace' => 0,
'UserId' => 0,
'Background' => 0,
'Concurrent' => 0,
'Query_json' => '',
@ -62,6 +63,7 @@ class Filter extends ZM_Object {
} # end foreach term
$this->_querystring .= $separator.urlencode($objectname.'[Query][sort_asc]').'='.$this->sort_asc();
$this->_querystring .= $separator.urlencode($objectname.'[Query][sort_field]').'='.$this->sort_field();
$this->_querystring .= $separator.urlencode($objectname.'[Query][skip_locked]').'='.$this->skip_locked();
$this->_querystring .= $separator.urlencode($objectname.'[Query][limit]').'='.$this->limit();
if ( $this->Id() ) {
$this->_querystring .= $separator.$objectname.urlencode('[Id]').'='.$this->Id();
@ -207,18 +209,29 @@ class Filter extends ZM_Object {
}
public function sort_asc( ) {
if ( func_num_args( ) ) {
if (func_num_args()) {
$Query = $this->Query();
$Query['sort_asc'] = func_get_arg(0);
$this->Query($Query);
}
if ( isset( $this->Query()['sort_asc'] ) ) {
if (isset($this->Query()['sort_asc'])) {
return $this->{'Query'}['sort_asc'];
}
return ZM_WEB_EVENT_SORT_ORDER == 'asc' ? 1 : 0;
#return $this->defaults{'sort_asc'};
}
public function skip_locked() {
if (func_num_args()) {
$Query = $this->Query();
$Query['skip_locked'] = func_get_arg(0);
$this->Query($Query);
}
if (isset($this->Query()['skip_locked']))
return $this->{'Query'}['skip_locked'];
return false;
}
public function limit( ) {
if ( func_num_args( ) ) {
$Query = $this->Query();

View File

@ -1,6 +1,8 @@
#header {
display: flex;
justify-content: space-between;
}
#sidebar {
min-width: 140px;
}
#menuControls {
@ -19,19 +21,22 @@
#monitorStatus {
margin: 4px auto;
text-align: center;
}
#monitorStatus #enableDisableAlarms {
float: left;
}
#monitorStatus #forceCancelAlarm {
float: right;
display: inline-block;
}
#monitorStatus #monitorState {
}
#replayStatus {
margin: 3px 0 2px;
text-align: center;
display: inline-block;
}
#replayStatus > span {
padding: 0 4px;
}
#dvrControls {
margin-top: 3px;
margin-bottom: 2px;
@ -67,15 +72,6 @@
cursor: default;
}
#replayStatus {
margin: 3px 0 2px;
text-align: center;
clear: both;
}
#replayStatus > span {
padding: 0 4px;
}
#events {
margin: 0 auto;
@ -116,3 +112,22 @@ span.alert {
background-color: #DCDCDC;
}
.controlHeader {
width: 100%;
}
#viewingFPS,
#captureFPS,
#analysisFPS
{
display: inline-block;
}
#stateValue,
#viewingFPSValue,
#captureFPSValue,
#analysisFPSValue
{
display: inline-block;
min-width: 40px;
text-align: right;
}

View File

@ -367,6 +367,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
<?php
# Note: The keys need to be actual column names
$sort_fields = array(
'' => translate('None'),
'Id' => translate('AttrId'),
'Name' => translate('AttrName'),
'Cause' => translate('AttrCause'),
@ -385,8 +386,16 @@ echo htmlSelect('filter[Query][sort_field]', $sort_fields, $filter->sort_field()
$sort_dirns = array(
'1' => translate('SortAsc'),
'0' => translate('SortDesc')
);
echo htmlSelect( 'filter[Query][sort_asc]', $sort_dirns, $filter->sort_asc() );
);
echo htmlSelect('filter[Query][sort_asc]', $sort_dirns, $filter->sort_asc());
?>
</td>
<td>
<label for="filter[Query][skip_locked]"><?php echo translate('Skip Locked') ?></label>
<?php
echo htmlSelect('filter[Query][skip_locked]',
array('0'=>translate('No'), '1'=>translate('Yes')),
$filter->skip_locked());
?>
</td>
<td>
@ -468,9 +477,13 @@ if ( ZM_OPT_MESSAGE ) {
<div id="optionsTable" class="filterTable">
<fieldset><legend><?php echo translate('Options') ?></legend>
<p>
<label for="background"><?php echo translate('BackgroundFilter') ?></label>
<label for="filter[Background]"><?php echo translate('BackgroundFilter') ?></label>
<input type="checkbox" id="filter[Background]" name="filter[Background]" value="1"<?php if ( $filter->Background() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/>
</p>
<p>
<label for="ExecuteInterval"><?php echo translate('Execute Interval') ?></label>
<input type="number" id="filter[ExecuteInterval]" name="filter[ExecuteInterval]" min="0" step="1" value="<?php echo $filter->ExecuteInterval() ?>" />
</p>
<p>
<label for="Concurrent"><?php echo translate('ConcurrentFilter') ?></label>
<input type="checkbox" id="filter[Concurrent]" name="filter[Concurrent]" value="1"<?php if ( $filter->Concurrent() ) { ?> checked="checked"<?php } ?> data-on-click-this="updateButtons"/>

View File

@ -44,7 +44,7 @@ function ajaxRequest(params) {
data.view = 'request';
data.request = 'watch';
data.mid = monitorId;
if ( auth_hash ) data.auth = auth_hash;
if (auth_hash) data.auth = auth_hash;
$j.getJSON(thisUrl, data)
.done(function(data) {
@ -94,8 +94,48 @@ function showPtzControls() {
showMode = 'control';
}
function changeSize() {
var width = $j('#width').val();
var height = $j('#height').val();
// Scale the frame
monitor_frame = $j('#imageFeed');
if (!monitor_frame) {
console.log('Error finding frame');
return;
}
if (width) monitor_frame.css('width', width);
if (height) monitor_frame.css('height', height);
var streamImg = document.getElementById('liveStream'+monitorData[monIdx].id);
if (streamImg) {
if (streamImg.nodeName == 'IMG') {
let src = streamImg.src;
streamImg.src = '';
src = src.replace(/width=[\.\d]+/i, 'width='+parseInt(width));
src = src.replace(/height=[\.\d]+/i, 'height='+parseInt(height));
src = src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src;
}
streamImg.style.width = width ? width : null;
streamImg.style.height = height ? height : null;
} else {
console.log('Did not find liveStream'+monitorData[monIdx].id);
}
$j('#scale').val('');
setCookie('zmCycleScale', '', 3600);
setCookie('zmCycleWidth', width, 3600);
setCookie('zmCycleHeight', height, 3600);
} // end function changeSize()
function changeScale() {
var scale = $j('#scale').val();
$j('#width').val('auto');
$j('#height').val('auto');
setCookie('zmCycleScale', scale, 3600);
setCookie('zmCycleWidth', 'auto', 3600);
setCookie('zmCycleHeight', 'auto', 3600);
var newWidth;
var newHeight;
var autoScale;
@ -104,7 +144,7 @@ function changeScale() {
// times and what the consequences would be
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
if (scale == '0' || scale == 'auto') {
var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
const newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
newWidth = newSize.width;
newHeight = newSize.height;
autoScale = newSize.autoScale;
@ -118,16 +158,18 @@ function changeScale() {
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
if (streamImg.nodeName == 'IMG') {
const oldSrc = streamImg.attr('src');
streamImg.attr('src', '');
// This is so that we don't waste bandwidth and let the browser do all the scaling.
if (autoScale > 100) autoScale = 100;
if (scale > 100) scale = 100;
var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale));
const newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale));
streamImg.width(newWidth);
streamImg.height(newHeight);
streamImg.attr('src', newSrc);
}
} else {
console.error('No element found for liveStream'+monitorId);
}
@ -200,9 +242,15 @@ function getStreamCmdResponse(respObj, respText) {
// The get status command can get backed up, in which case we won't be able to get the semaphore and will exit.
if (respObj.status) {
streamStatus = respObj.status;
$j('#fpsValue').text(streamStatus.fps);
$j('#capturefpsValue').text(streamStatus.capturefps);
$j('#analysisfpsValue').text(streamStatus.analysisfps);
if ($j('#viewingFPSValue').text() != streamStatus.fps) {
$j('#viewingFPSValue').text(streamStatus.fps);
}
if ($j('#captureFPSValue').text() != streamStatus.capturefps) {
$j('#captureFPSValue').text(streamStatus.capturefps);
}
if ($j('#analysisFPSValue').text() != streamStatus.analysisfps) {
$j('#analysisFPSValue').text(streamStatus.analysisfps);
}
setAlarmState(streamStatus.state);
@ -287,31 +335,19 @@ function getStreamCmdResponse(respObj, respText) {
// Try to reload the image stream.
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
const oldSrc = streamImg.attr('src');
if (oldSrc) {
const newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
if (oldSrc != newSrc) {
streamImg.attr('src', newSrc);
table.bootstrapTable('refresh');
}
}
}
} // end if have a new auth hash
} // end if respObj.status
} else {
checkStreamForErrors('getStreamCmdResponse', respObj);//log them
// Try to reload the image stream.
// If it's an auth error, we should reload the whole page.
console.log("have error");
//window.location.reload();
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.attr('src', newSrc);
console.log('Changing livestream src to ' + newSrc);
} else {
console.log('Unable to find streamImg liveStream');
}
}
var streamCmdTimeout = statusRefreshTimeout;
@ -368,6 +404,7 @@ function streamCmdPlay(action) {
}
function streamCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=stream&connkey='+connKey, data)
.done(getStreamCmdResponse)
.fail(getStreamCmdError);
@ -386,10 +423,7 @@ function streamCmdStop(action) {
setButtonState('fastRevBtn', 'unavail');
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_STOP;
streamCmdReq(data);
streamCmdReq({command: CMD_STOP});
}
setButtonState('stopBtn', 'unavail');
setButtonState('playBtn', 'active');
@ -407,7 +441,6 @@ function streamCmdFastFwd(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTFWD;
streamCmdReq(data);
}
@ -425,7 +458,6 @@ function streamCmdSlowFwd(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWFWD;
streamCmdReq(data);
}
@ -447,7 +479,6 @@ function streamCmdSlowRev(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWREV;
streamCmdReq(data);
}
@ -469,7 +500,6 @@ function streamCmdFastRev(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTREV;
streamCmdReq(data);
}
@ -477,7 +507,6 @@ function streamCmdFastRev(action) {
function streamCmdZoomIn(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_ZOOMIN;
@ -486,14 +515,12 @@ function streamCmdZoomIn(x, y) {
function streamCmdZoomOut() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_ZOOMOUT;
streamCmdReq(data);
}
function streamCmdScale(scale) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SCALE;
data.scale = scale;
streamCmdReq(data);
@ -501,7 +528,6 @@ function streamCmdScale(scale) {
function streamCmdPan(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_PAN;
@ -510,11 +536,11 @@ function streamCmdPan(x, y) {
function streamCmdQuery() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_QUERY;
streamCmdReq(data);
}
/* getStatusCmd is used when not streaming, since there is no persistent zms */
function getStatusCmdResponse(respObj, respText) {
watchdogOk('status');
if (statusCmdTimer) {
@ -522,32 +548,33 @@ function getStatusCmdResponse(respObj, respText) {
}
if (respObj.result == 'Ok') {
$j('#fpsValue').text(respObj.monitor.FrameRate);
$j('#captureFPSValue').text(respObj.monitor.FrameRate);
setAlarmState(respObj.monitor.Status);
} else {
checkStreamForErrors('getStatusCmdResponse', respObj);
}
var statusCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) {
if (alarmState == STATE_ALARM || alarmState == STATE_ALERT) {
statusCmdTimeout = statusCmdTimeout/5;
}
statusCmdTimer = setTimeout(statusCmdQuery, statusCmdTimeout);
}
function statusCmdQuery() {
$j.getJSON(monitorUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId)
$j.getJSON(monitorUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId+'&'+auth_relay)
.done(getStatusCmdResponse)
.fail(logAjaxFail);
streamCmdTimer = null;
statusCmdTimer = null;
}
function alarmCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=alarm&id='+monitorId, data)
.done(getAlarmCmdResponse)
.fail(function(jqxhr, textStatus, error) {
if ( textStatus === "timeout" ) {
if (textStatus === 'timeout') {
streamCmdQuery();
} else {
logAjaxFail(jqxhr, textStatus, error);
@ -561,14 +588,12 @@ function getAlarmCmdResponse(respObj, respText) {
function cmdDisableAlarms() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'disableAlarms';
alarmCmdReq(data);
}
function cmdEnableAlarms() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'enableAlarms';
alarmCmdReq(data);
}
@ -583,7 +608,6 @@ function cmdAlarm() {
function cmdForceAlarm() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'forceAlarm';
alarmCmdReq(data);
if (window.event) window.event.preventDefault();
@ -591,7 +615,6 @@ function cmdForceAlarm() {
function cmdCancelForcedAlarm() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'cancelForcedAlarm';
alarmCmdReq(data);
if (window.event) window.event.preventDefault();
@ -607,6 +630,7 @@ function cmdForce() {
}
function controlReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=control&id='+monitorId, data)
.done(getControlResponse)
.fail(logAjaxFail);
@ -639,16 +663,16 @@ function controlCmd(event) {
var data = {};
if (event && (xtell || ytell)) {
var target = event.target;
var offset = $j(target).offset();
var width = $j(target).width();
var height = $j(target).height();
const target = event.target;
const offset = $j(target).offset();
const width = $j(target).width();
const height = $j(target).height();
var x = event.pageX - offset.left;
var y = event.pageY - offset.top;
const x = event.pageX - offset.left;
const y = event.pageY - offset.top;
if (xtell) {
var xge = parseInt((x*100)/width);
let xge = parseInt((x*100)/width);
if (xtell == -1) {
xge = 100 - xge;
} else if (xtell == 2) {
@ -657,7 +681,7 @@ function controlCmd(event) {
data.xge = xge;
}
if (ytell) {
var yge = parseInt((y*100)/height);
let yge = parseInt((y*100)/height);
if (ytell == -1) {
yge = 100 - yge;
} else if (ytell == 2) {
@ -667,7 +691,6 @@ function controlCmd(event) {
}
}
if (auth_hash) data.auth = auth_hash;
data.control = control;
controlReq(data);
@ -678,7 +701,6 @@ function controlCmd(event) {
function controlCmdImage(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.scale = scale;
data.control = imageControlMode;
data.x = x;
@ -768,7 +790,6 @@ function reloadWebSite() {
function updatePresetLabels() {
var lblNdx = $j('#ctrlPresetForm option:selected').val();
$j('#newLabel').val(labels[lblNdx]);
}
@ -878,7 +899,7 @@ function initPage() {
// Load the PTZ Preset modal into the DOM
if (monitorControllable) getCtrlPresetModal();
// Load the settings modal into the DOM
if (monitorType == "Local") getSettingsModal();
if (monitorType == 'Local') getSettingsModal();
}
if (monitorType != 'WebSite') {
@ -886,15 +907,12 @@ function initPage() {
statusCmdTimer = setTimeout(statusCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
} else {
streamCmdTimer = setTimeout(streamCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
}
if (canStreamNative || (streamMode == 'single')) {
var streamImg = $j('#imageFeed img');
if (!streamImg) {
streamImg = $j('#imageFeed object');
}
if (!streamImg) streamImg = $j('#imageFeed object');
if (!streamImg) {
console.error('No streamImg found for imageFeed');
} else {
@ -905,6 +923,10 @@ function initPage() {
streamImg.click(function(event) {
handleClick(event);
});
streamImg.on("error", function(thing) {
console.log("Error loading image");
console.log(thing);
});
}
} // end if have streamImg
} // streamMode native or single
@ -944,6 +966,18 @@ function initPage() {
$j('#settingsModal').modal('show');
});
bindButton('#cyclePlayBtn', 'click', null, cycleStart);
bindButton('#cyclePauseBtn', 'click', null, cyclePause);
bindButton('#cycleNextBtn', 'click', null, cycleNext);
bindButton('#cyclePrevBtn', 'click', null, cyclePrev);
bindButton('#cycleToggle', 'click', null, cycleToggle);
bindButton('#cyclePeriod', 'change', null, cyclePeriodChange);
if (cycle) {
cycleStart();
} else {
cyclePause();
}
// Only enable the settings button for local cameras
settingsBtn.prop('disabled', !(canView.Control && (monitorType == 'Local')));
@ -972,7 +1006,6 @@ function initPage() {
function watchFullscreen() {
const btn = document.getElementById('fullscreenBtn');
console.log(btn);
if (btn.firstElementChild.innerHTML=='fullscreen') {
const content = document.getElementById('content');
openFullscreen(content);
@ -985,5 +1018,70 @@ function watchFullscreen() {
}
}
var intervalId;
var secondsToCycle = 0;
function nextCycleView() {
secondsToCycle --;
if (secondsToCycle<=0) {
window.location.replace('?view=watch&mid='+nextMid+'&mode='+mode+'&cycle=true');
}
$j('#secondsToCycle').text(secondsToCycle);
}
function cyclePause() {
clearInterval(intervalId);
$j('#cyclePauseBtn').hide();
$j('#cyclePlayBtn').show();
}
function cycleStart() {
secondsToCycle = $j('#cyclePeriod').val();
intervalId = setInterval(nextCycleView, 1000);
$j('#cyclePauseBtn').show();
$j('#cyclePlayBtn').hide();
}
function cycleNext() {
monIdx ++;
if (monIdx >= monitorData.length) {
monIdx = 0;
}
if (!monitorData[monIdx]) {
console.log('No monitorData for ' + monIdx);
}
window.location.replace('?view=watch&cycle=true&mid='+monitorData[monIdx].id+'&mode='+mode);
}
function cyclePrev() {
monIdx --;
if (monIdx < 0) {
monIdx = monitorData.length - 1;
}
if (!monitorData[monIdx]) {
console.log('No monitorData for ' + monIdx);
}
window.location.replace('?view=watch&cycle=true&mid='+monitorData[monIdx].id+'&mode='+mode);
}
function cyclePeriodChange() {
const cyclePeriodSelect = $j('#cyclePeriod');
secondsToCycle = cyclePeriodSelect.val();
setCookie('zmCyclePeriod', secondsToCycle, 3600);
}
function cycleToggle(e) {
sidebar = $j('#sidebar');
button = $j('#cycleToggle');
if (sidebar.is(":visible")) {
sidebar.hide();
setCookie('zmCycleShow', false, 3600);
} else {
sidebar.show();
setCookie('zmCycleShow', true, 3600);
}
button.toggleClass('btn-secondary');
button.toggleClass('btn-primary');
}
// Kick everything off
$j(document).ready(initPage);

View File

@ -1,21 +1,20 @@
<?php
global $monIdx;
global $nextMid;
global $options;
global $monitors;
global $streamMode;
global $showPtzControls;
global $connkey;
global $monitor;
global $scale;
global $labels;
global $cycle;
?>
//
// Import constants
//
var deleteString = "<?php echo translate('Delete') ?>";
var enableAlarmsStr = "<?php echo translate('EnableAlarms') ?>";
var disableAlarmsStr = "<?php echo translate('DisableAlarms') ?>";
var forceAlarmStr = "<?php echo translate('ForceAlarm') ?>";
var cancelForcedAlarmStr = "<?php echo translate('CancelForcedAlarm') ?>";
var CMD_NONE = <?php echo CMD_NONE ?>;
var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
@ -34,14 +33,13 @@ var CMD_NEXT = <?php echo CMD_NEXT ?>;
var CMD_SEEK = <?php echo CMD_SEEK ?>;
var CMD_QUERY = <?php echo CMD_QUERY ?>;
var SCALE_BASE = <?php echo SCALE_BASE ?>;
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>;
var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>;
var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
var streamMode = "<?php echo $streamMode ?>";
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";
var cycle = <?php echo $cycle ? 'true' : 'false' ?>;
var connKey = '<?php echo $connkey ?>';
var maxDisplayEvents = <?php echo 2 * MAX_EVENTS ?>;
@ -55,6 +53,28 @@ var monitorRefresh = '<?php echo $monitor->Refresh() ?>';
var monitorStreamReplayBuffer = <?php echo $monitor->StreamReplayBuffer() ?>;
var monitorControllable = <?php echo $monitor->Controllable()?'true':'false' ?>;
var monIdx = '<?php echo $monIdx; ?>';
var nextMid = "<?php echo isset($nextMid)?$nextMid:'' ?>";
var mode = "<?php echo $options['mode'] ?>";
var monitorData = new Array();
<?php
foreach ($monitors as $monitor) {
?>
monitorData[monitorData.length] = {
'id': <?php echo $monitor->Id() ?>,
'width': <?php echo $monitor->ViewWidth() ?>,
'height':<?php echo $monitor->ViewHeight() ?>,
'url': '<?php echo $monitor->UrlToIndex() ?>',
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
'type': '<?php echo $monitor->Type() ?>',
'refresh': '<?php echo $monitor->Refresh() ?>'
};
<?php
} // end foreach monitor
?>
var SCALE_BASE = <?php echo SCALE_BASE ?>;
var scale = '<?php echo $scale ?>';
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
@ -63,17 +83,16 @@ var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
<?php
$control = $monitor->Control();
if ( $control->CanMoveMap() ) { ?>
var imageControlMode = "moveMap";
<?php } elseif ( $control->CanMoveRel() ) { ?>
var imageControlMode = "movePseudoMap";
<?php } elseif ( $control->CanMoveCon() ) { ?>
var imageControlMode = "moveConMap";
<?php } else { ?>
var imageControlMode = null;
<?php } ?>
var imageControlMode = '<?php
$control = $monitor->Control();
if ($control->CanMoveMap()) {
echo 'moveMap';
} else if ($control->CanMoveRel()) {
echo 'movePseudoMap';
} else if ($control->CanMoveCon()) {
echo 'moveConMap';
}
?>';
var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>;
var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
@ -81,17 +100,18 @@ var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
var labels = new Array();
<?php
$labels = array();
foreach( dbFetchAll( 'SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array( $monitor->Id() ) ) as $row ) {
$labels[$row['Preset']] = $row['Label'];
}
foreach ($labels as $index=>$label) {
?>
labels[<?php echo validInt($index) ?>] = '<?php echo validJsStr($label) ?>';
<?php
foreach (dbFetchAll('SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array($monitor->Id())) as $row) {
$label = $labels[$row['Preset']] = $row['Label'];
echo 'labels['. validInt($index) .'] = '.validJsStr($label).'\'';
}
?>
var deleteString = "<?php echo translate('Delete') ?>";
var enableAlarmsStr = "<?php echo translate('EnableAlarms') ?>";
var disableAlarmsStr = "<?php echo translate('DisableAlarms') ?>";
var forceAlarmStr = "<?php echo translate('ForceAlarm') ?>";
var cancelForcedAlarmStr = "<?php echo translate('CancelForcedAlarm') ?>";
var translate = {
"seconds": "<?php echo translate('seconds') ?>",
"Fullscreen": "<?php echo translate('Fullscreen') ?>",
"Exit Fullscreen": "<?php echo translate('Exit Fullscreen') ?>",
};

View File

@ -18,99 +18,258 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView('Stream') ) {
if (!canView('Stream')) {
$view = 'error';
return;
}
require_once('includes/Monitor.php');
ob_start();
include('_monitor_filters.php');
$filterbar = ob_get_contents();
ob_end_clean();
if ( !isset($_REQUEST['mid']) ) {
$view = 'error';
return;
}
// This is for input sanitation
$mid = intval($_REQUEST['mid']);
if ( !visibleMonitor($mid) ) {
$mid = isset($_REQUEST['mid']) ? intval($_REQUEST['mid']) : 0;
$widths = array(
'auto' => translate('auto'),
'100%' => '100%',
'160px' => '160px',
'320px' => '320px',
'352px' => '352px',
'640px' => '640px',
'1280px' => '1280px',
'1920px' => '1920px'
);
$heights = array(
'auto' => translate('auto'),
'240px' => '240px',
'480px' => '480px',
'720px' => '720px',
'1080px' => '1080px',
);
$monitors = array();
$monitor_index = 0;
foreach ($displayMonitors as &$row) {
if ($row['Function'] == 'None') continue;
if ($mid and ($row['Id'] == $mid)) $monitor_index = count($monitors);
$monitors[] = new ZM\Monitor($row);
if (!isset($widths[$row['Width'].'px'])) {
$widths[$row['Width'].'px'] = $row['Width'].'px';
}
if (!isset($heights[$row['Height'].'px'])) {
$heights[$row['Height'].'px'] = $row['Height'].'px';
}
unset($row);
} # end foreach Monitor
if (!$mid) {
$mid = $monitors[0]->Id();
$monitor_index = 0;
}
if (!visibleMonitor($mid)) {
$view = 'error';
return;
}
require_once('includes/Monitor.php');
$monitor = new ZM\Monitor($mid);
$nextMid = ($monitor_index == count($monitors)-1) ? $monitors[0]->Id() : $monitors[$monitor_index+1]->Id();
$cycle = isset($_REQUEST['cycle']) and ($_REQUEST['cycle'] == 'true');
$showCycle = $cycle;
ZM\Error("Show cycle: $showCycle");
if (isset($_COOKIE['zmCycleShow'])) {
$showCycle = $_COOKIE['zmCycleShow'] == 'true';
ZM\Error("Show cycle: $showCycle");
} else {
ZM\Error("Show cycle: not set");
}
#Whether to show the controls button
$showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' );
if ( isset($_REQUEST['scale']) ) {
$options = array();
if (empty($_REQUEST['mode'])) {
$options['mode'] = canStream() ? 'stream' : 'still';
} else {
$options['mode'] = validHtmlStr($_REQUEST['mode']);
}
zm_session_start();
$period = ZM_WEB_REFRESH_CYCLE;
if (isset($_REQUEST['period'])) {
$period = validInt($_REQUEST['period']);
} else if (isset($_COOKIE['zmCyclePeriod'])) {
$period = validInt($_COOKIE['zmCyclePeriod']);
}
if (isset($_REQUEST['scale'])) {
$scale = validInt($_REQUEST['scale']);
} else if ( isset($_COOKIE['zmWatchScale'.$mid]) ) {
$scale = $_COOKIE['zmWatchScale'.$mid];
} else {
$scale = $monitor->DefaultScale();
}
$options['scale'] = $scale;
if (isset($_REQUEST['width'])) {
$options['width'] = validInt($_REQUEST['width']);
} else if ( isset($_COOKIE['zmCycleWidth']) and $_COOKIE['zmCycleWidth'] ) {
$_SESSION['zmCycleWidth'] = $options['width'] = $_COOKIE['zmCycleWidth'];
#} elseif ( isset($_SESSION['zmCycleWidth']) and $_SESSION['zmCycleWidth'] ) {
#$options['width'] = $_SESSION['zmCycleWidth'];
} else {
$options['width'] = '';
}
if (isset($_REQUEST['height'])) {
$options['height'] =validInt($_REQUEST['height']);
} else if (isset($_COOKIE['zmCycleHeight']) and $_COOKIE['zmCycleHeight']) {
$_SESSION['zmCycleHeight'] = $options['height'] = $_COOKIE['zmCycleHeight'];
#else if ( isset($_SESSION['zmCycleHeight']) and $_SESSION['zmCycleHeight'] )
#$options['height'] = $_SESSION['zmCycleHeight'];
} else {
$options['height'] = '';
}
session_write_close();
$connkey = generateConnKey();
$streamMode = getStreamMode();
$popup = ((isset($_REQUEST['popup'])) && ($_REQUEST['popup'] == 1));
noCacheHeaders();
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
?>
<body>
<?php echo getNavBarHTML() ?>
<div id="header">
<div class="controlHeader">
<form method="get">
<input type="hidden" name="view" value="watch"/>
<?php echo $filterbar ?>
</form>
</div>
<div class="d-flex flex-row justify-content-between px-3 py-1">
<div>
<div id="navButtons">
<button type="button" id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button type="button" id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<button type="button" id="settingsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Settings') ?>" disabled><i class="fa fa-sliders"></i></button>
<button type="button" id="enableAlmBtn" class="btn btn-normal" data-on-click="cmdAlarm" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DisableAlarms') ?>" disabled><i class="fa fa-bell"></i></button>
<button type="button" id="forceAlmBtn" class="btn btn-danger" data-on-click="cmdForce" data-toggle="tooltip" data-placement="top" title="<?php echo translate('ForceAlarm') ?>" disabled><i class="fa fa-exclamation-circle"></i></button>
</div>
<div>
<h2><?php echo makeLink('?view=monitor&amp;mid='.$monitor->Id(), validHtmlStr($monitor->Name()), canEdit('Monitors')) ?></h2>
</div>
<div>
<?php echo translate('Scale').': '.htmlSelect('scale', $scales, $scale, array('id'=>'scale')); ?>
</div>
<div id="headerButtons">
<!--
<?php if ( $options['mode'] == 'stream' ) { ?>
<a href="?view=<?php echo $view ?>&amp;mode=still&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stills') ?></a>
<?php } else { ?>
<a href="?view=<?php echo $view ?>&amp;mode=stream&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stream') ?></a>
<?php } ?>
-->
<button type="button" id="cycleToggle" class="btn <?php echo $showCycle ? 'btn-primary':'btn-secondary'?>" title="<?php echo translate('Toggle cycle sidebar')?>">
<span class="material-icons md-18">view_carousel</span>
</button>
</div>
<div id="sizeControl">
<span id="widthControl">
<label><?php echo translate('Width') ?>:</label>
<?php echo htmlSelect('width', $widths, $options['width'], array('id'=>'width', 'data-on-change-this'=>'changeSize') ); ?>
</span>
<span id="heightControl">
<label><?php echo translate('Height') ?>:</label>
<?php echo htmlSelect('height', $heights, $options['height'], array('id'=>'height', 'data-on-change-this'=>'changeSize') ); ?>
</span>
<span id="scaleControl">
<label><?php echo translate('Scale') ?>:</label>
<?php echo htmlSelect('scale', $scales, $options['scale'], array('id'=>'scale', 'data-on-change-this'=>'changeScale') ); ?>
</span>
</div><!--sizeControl-->
</div><!--control header-->
</div><!--header-->
<?php
if ( $monitor->Status() != 'Connected' and $monitor->Type() != 'WebSite' ) {
echo '<div class="warning">Monitor is not capturing. We will be unable to provide an image</div>';
}
?>
<div id="content">
<div class="container-fluid h-100">
<div class="row flex-nowrap h-100" id="content">
<nav id="sidebar" class="h-100"<?php echo $showCycle?'':' style="display:none;"'?>>
<div id="cycleButtons" class="buttons">
<?php
$seconds = translate('seconds');
$minute = translate('minute');
$minutes = translate('minutes');
$cyclePeriodOptions = array(
10 => '10 '.$seconds,
30 => '30 '.$seconds,
60 => '1 '.$minute,
120 => '2 '.$minutes,
300 => '5 '.$minutes,
);
if (!isset($cyclePeriodOptions[ZM_WEB_REFRESH_CYCLE])) {
$cyclePeriodOptions[ZM_WEB_REFRESH_CYCLE] = ZM_WEB_REFRESH_CYCLE.' '.$seconds;
}
echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cyclePeriod'));
?>
<span id="secondsToCycle"></span><br/>
<button type="button" id="cyclePrevBtn" title="<?php echo translate('PreviousMonitor') ?>">
<i class="material-icons md-18">skip_previous</i>
</button>
<button type="button" id="cyclePauseBtn" title="<?php echo translate('PauseCycle') ?>">
<i class="material-icons md-18">pause</i>
</button>
<button type="button" id="cyclePlayBtn" title="<?php echo translate('PlayCycle') ?>">
<i class="material-icons md-18">play_arrow</i>
</button>
<button type="button" id="cycleNextBtn" title="<?php echo translate('NextMonitor') ?>">
<i class="material-icons md-18">skip_next</i>
</button>
</div>
<ul class="nav nav-pills flex-column h-100">
<?php
foreach ($monitors as $m) {
echo '<li class="nav-item"><a class="nav-link'.( $m->Id() == $monitor->Id() ? ' active' : '' ).'" href="?view=watch&amp;mid='.$m->Id().'">'.$m->Name().'</a></li>';
}
?>
</ul>
</nav>
<div class="container-fluid col-sm-offset-2 h-100 pr-0">
<div id="imageFeed"
<?php
if ( $streamMode == 'jpeg' ) {
if ($streamMode == 'jpeg') {
echo 'title="Click to zoom, shift click to pan, ctrl click to zoom out"';
}
?>
><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?></div>
><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?>
</div>
<?php if ( $monitor->Type() != 'WebSite' ) { ?>
<?php if ($monitor->Type() != 'WebSite') { ?>
<div id="monitorStatus">
<div id="monitorState">
<?php echo translate('State') ?>:
<span id="stateValue"></span> -
<span title="<?php echo translate('Viewing FPS')?>"><span id="fpsValue"></span> fps</span>
<span title="<?php echo translate('Capturing FPS')?>"><span id="capturefpsValue"></span> fps</span>
<span><?php echo translate('State') ?>:<span id="stateValue"></span></span>
<span id="viewingFPS" title="<?php echo translate('Viewing FPS')?>"><span id="viewingFPSValue"></span> fps</span>
<span id="captureFPS" title="<?php echo translate('Capturing FPS')?>"><span id="captureFPSValue"></span> fps</span>
<?php if ( $monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord' ) { ?>
<span title="<?php echo translate('Analysis FPS')?>"><span id="analysisfpsValue"></span> fps</span>
<span id="analysisFPS" title="<?php echo translate('Analysis FPS')?>"><span id="analysisFPSValue"></span> fps</span>
<?php } ?>
</div>
</div>
<div id="replayStatus"<?php echo $streamMode=="single" ? ' class="hidden"' : '' ?>>
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue"></span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
<span id="delay"><?php echo translate('Delay') ?>: <span id="delayValue"></span>s</span>
<span id="level"><?php echo translate('Buffer') ?>: <span id="levelValue"></span>%</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div>
<div class="buttons">
<div id="dvrControls">
<?php
if ( $streamMode == 'jpeg' ) {
if ( $monitor->StreamReplayBuffer() != 0 ) {
if ($streamMode == 'jpeg') {
if ($monitor->StreamReplayBuffer() != 0) {
?>
<button type="button" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastRev">
<i class="material-icons md-18">fast_rewind</i>
<i class="material-icons md-18">fast_rewind</i>
</button>
<button type="button" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowRev">
<i class="material-icons md-18">chevron_right</i>
@ -119,22 +278,22 @@ if ( $streamMode == 'jpeg' ) {
}
?>
<button type="button" id="pauseBtn" title="<?php echo translate('Pause') ?>" class="inactive" data-on-click-true="streamCmdPause">
<i class="material-icons md-18">pause</i>
<i class="material-icons md-18">pause</i>
</button>
<button type="button" id="stopBtn" title="<?php echo translate('Stop') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdStop" style="display:none;">
<i class="material-icons md-18">stop</i>
<i class="material-icons md-18">stop</i>
</button>
<button type="button" id="playBtn" title="<?php echo translate('Play') ?>" class="active" disabled="disabled" data-on-click-true="streamCmdPlay">
<i class="material-icons md-18">play_arrow</i>
<i class="material-icons md-18">play_arrow</i>
</button>
<?php
if ( $monitor->StreamReplayBuffer() != 0 ) {
if ($monitor->StreamReplayBuffer() != 0) {
?>
<button type="button" id="slowFwdBtn" title="<?php echo translate('StepForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowFwd">
<i class="material-icons md-18">chevron_right</i>
</button>
<button type="button" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastFwd">
<i class="material-icons md-18">fast_forward</i>
<i class="material-icons md-18">fast_forward</i>
</button>
<?php
}
@ -148,13 +307,7 @@ if ( $streamMode == 'jpeg' ) {
<?php
} // end if streamMode==jpeg
?>
</div>
<div id="replayStatus"<?php echo $streamMode=="single" ? ' class="hidden"' : '' ?>>
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue"></span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
<span id="delay"><?php echo translate('Delay') ?>: <span id="delayValue"></span>s</span>
<span id="level"><?php echo translate('Buffer') ?>: <span id="levelValue"></span>%</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div><!--dvrButtons-->
</div>
<?php } // end if $monitor->Type() != 'WebSite' ?>
<?php
@ -208,6 +361,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
</table>
</div>
</div>
<?php
}
if ( ZM_WEB_SOUND_ON_ALARM ) {