Merge branch 'master' of github.com:ZoneMinder/zoneminder
This commit is contained in:
commit
76b75cc69b
|
@ -28,8 +28,8 @@ SET @s = (SELECT IF(
|
||||||
AND table_name = 'Monitors'
|
AND table_name = 'Monitors'
|
||||||
AND column_name = 'TotalEvents'
|
AND column_name = 'TotalEvents'
|
||||||
) > 0,
|
) > 0,
|
||||||
"SELECT 'Column TotalEvents is already removed from Monitors'",
|
"ALTER TABLE `Monitors` DROP `TotalEvents`",
|
||||||
"ALTER TABLE `Monitors` DROP `TotalEvents`"
|
"SELECT 'Column TotalEvents is already removed from Monitors'"
|
||||||
));
|
));
|
||||||
PREPARE stmt FROM @s;
|
PREPARE stmt FROM @s;
|
||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
|
@ -50,8 +50,8 @@ SET @s = (SELECT IF(
|
||||||
AND table_name = 'Monitors'
|
AND table_name = 'Monitors'
|
||||||
AND column_name = 'TotalEventDiskSpace'
|
AND column_name = 'TotalEventDiskSpace'
|
||||||
) > 0,
|
) > 0,
|
||||||
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'",
|
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`",
|
||||||
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`"
|
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'"
|
||||||
));
|
));
|
||||||
PREPARE stmt FROM @s;
|
PREPARE stmt FROM @s;
|
||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
|
|
|
@ -3,6 +3,50 @@ Debian
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
Easy Way: Debian 11 (Bullseye)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
|
||||||
|
|
||||||
|
**Step 1:** Setup Sudo (optional but recommended)
|
||||||
|
|
||||||
|
By default Debian does not come with sudo, so you have to install it and configure it manually.
|
||||||
|
This step is optional but recommended and the following instructions assume that you have setup sudo.
|
||||||
|
If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install sudo
|
||||||
|
usermod -a -G sudo <username>
|
||||||
|
exit
|
||||||
|
|
||||||
|
Now your terminal session is back under your normal user. You can check that
|
||||||
|
you are now part of the sudo group with the command ``groups``, "sudo" should
|
||||||
|
appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
|
||||||
|
|
||||||
|
**Step 2:** Update system and install zoneminder
|
||||||
|
|
||||||
|
Run the following commands.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
sudo apt install mariadb-server
|
||||||
|
sudo apt install zoneminder
|
||||||
|
|
||||||
|
When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``.
|
||||||
|
|
||||||
|
**Step 3:** Setup permissions for zm.conf
|
||||||
|
|
||||||
|
To make sure zoneminder can read the configuration file, run the following command.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo chgrp -c www-data /etc/zm/zm.conf
|
||||||
|
|
||||||
|
Congratulations! You should now be able to access zoneminder at ``http://yourhostname/zm``
|
||||||
|
|
||||||
Easy Way: Debian Buster
|
Easy Way: Debian Buster
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ An Easy To Use Docker Image
|
||||||
===========================
|
===========================
|
||||||
If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories:
|
If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories:
|
||||||
|
|
||||||
* If you want to run the latest stable release, please use his `zoneminder repository <https://github.com/dlandon/zoneminder>`__.
|
* If you want to run the latest stable release, please use his `zoneminder machine learning repository <https://github.com/dlandon/zoneminder.machine.learning>`__.
|
||||||
* If you want to run the latest zoneminder master, please use his `zoneminder master repository <https://github.com/dlandon/zoneminder.master-docker>`__.
|
* If you want to run the latest zoneminder master, please use his `zoneminder master repository <https://github.com/dlandon/zoneminder.master-docker>`__.
|
||||||
|
|
||||||
In both cases, instructions are provided in the repo README files.
|
In both cases, instructions are provided in the repo README files.
|
||||||
|
|
|
@ -1064,7 +1064,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_FFMPEG_FORMATS',
|
name => 'ZM_FFMPEG_FORMATS',
|
||||||
default => 'mpg mpeg wmv asf avi* mov swf 3gp**',
|
default => 'mp4* mpg mpeg wmv asf avi mov swf 3gp**',
|
||||||
description => 'Formats to allow for ffmpeg video generation',
|
description => 'Formats to allow for ffmpeg video generation',
|
||||||
help => q`
|
help => q`
|
||||||
Ffmpeg can generate video in many different formats. This
|
Ffmpeg can generate video in many different formats. This
|
||||||
|
|
|
@ -29,6 +29,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
require ZoneMinder::Base;
|
require ZoneMinder::Base;
|
||||||
|
require ZoneMinder::Object;
|
||||||
require ZoneMinder::Monitor;
|
require ZoneMinder::Monitor;
|
||||||
|
|
||||||
our $VERSION = $ZoneMinder::Base::VERSION;
|
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
|
@ -42,24 +43,116 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Database qw(:all);
|
use ZoneMinder::Database qw(:all);
|
||||||
|
|
||||||
|
use parent qw(ZoneMinder::Object);
|
||||||
|
|
||||||
|
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||||
|
$table = 'Controls';
|
||||||
|
$serial = $primary_key = 'Id';
|
||||||
|
%fields = map { $_ => $_ } qw(
|
||||||
|
Id
|
||||||
|
Name
|
||||||
|
Type
|
||||||
|
Protocol
|
||||||
|
CanWake
|
||||||
|
CanSleep
|
||||||
|
CanReset
|
||||||
|
CanReboot
|
||||||
|
CanZoom
|
||||||
|
CanAutoZoom
|
||||||
|
CanZoomAbs
|
||||||
|
CanZoomRel
|
||||||
|
CanZoomCon
|
||||||
|
MinZoomRange
|
||||||
|
MaxZoomRange
|
||||||
|
MinZoomStep
|
||||||
|
MaxZoomStep
|
||||||
|
HasZoomSpeed
|
||||||
|
MinZoomSpeed
|
||||||
|
MaxZoomSpeed
|
||||||
|
CanFocus
|
||||||
|
CanAutoFocus
|
||||||
|
CanFocusAbs
|
||||||
|
CanFocusRel
|
||||||
|
CanFocusCon
|
||||||
|
MinFocusRange
|
||||||
|
MaxFocusRange
|
||||||
|
MinFocusStep
|
||||||
|
MaxFocusStep
|
||||||
|
HasFocusSpeed
|
||||||
|
MinFocusSpeed
|
||||||
|
MaxFocusSpeed
|
||||||
|
CanIris
|
||||||
|
CanAutoIris
|
||||||
|
CanIrisAbs
|
||||||
|
CanIrisRel
|
||||||
|
CanIrisCon
|
||||||
|
MinIrisRange
|
||||||
|
MaxIrisRange
|
||||||
|
MinIrisStep
|
||||||
|
MaxIrisStep
|
||||||
|
HasIrisSpeed
|
||||||
|
MinIrisSpeed
|
||||||
|
MaxIrisSpeed
|
||||||
|
CanGain
|
||||||
|
CanAutoGain
|
||||||
|
CanGainAbs
|
||||||
|
CanGainRel
|
||||||
|
CanGainCon
|
||||||
|
MinGainRange
|
||||||
|
MaxGainRange
|
||||||
|
MinGainStep
|
||||||
|
MaxGainStep
|
||||||
|
HasGainSpeed
|
||||||
|
MinGainSpeed
|
||||||
|
MaxGainSpeed
|
||||||
|
CanWhite
|
||||||
|
CanAutoWhite
|
||||||
|
CanWhiteAbs
|
||||||
|
CanWhiteRel
|
||||||
|
CanWhiteCon
|
||||||
|
MinWhiteRange
|
||||||
|
MaxWhiteRange
|
||||||
|
MinWhiteStep
|
||||||
|
MaxWhiteStep
|
||||||
|
HasWhiteSpeed
|
||||||
|
MinWhiteSpeed
|
||||||
|
MaxWhiteSpeed
|
||||||
|
HasPresets
|
||||||
|
NumPresets
|
||||||
|
HasHomePreset
|
||||||
|
CanSetPresets
|
||||||
|
CanMove
|
||||||
|
CanMoveDiag
|
||||||
|
CanMoveMap
|
||||||
|
CanMoveAbs
|
||||||
|
CanMoveRel
|
||||||
|
CanMoveCon
|
||||||
|
CanPan
|
||||||
|
MinPanRange
|
||||||
|
MaxPanRange
|
||||||
|
MinPanStep
|
||||||
|
MaxPanStep
|
||||||
|
HasPanSpeed
|
||||||
|
MinPanSpeed
|
||||||
|
MaxPanSpeed
|
||||||
|
HasTurboPan
|
||||||
|
TurboPanSpeed
|
||||||
|
CanTilt
|
||||||
|
MinTiltRange
|
||||||
|
MaxTiltRange
|
||||||
|
MinTiltStep
|
||||||
|
MaxTiltStep
|
||||||
|
HasTiltSpeed
|
||||||
|
MinTiltSpeed
|
||||||
|
MaxTiltSpeed
|
||||||
|
HasTurboTilt
|
||||||
|
TurboTiltSpeed
|
||||||
|
CanAutoScan
|
||||||
|
NumScanPaths
|
||||||
|
);
|
||||||
|
|
||||||
our $AUTOLOAD;
|
our $AUTOLOAD;
|
||||||
|
|
||||||
sub new {
|
|
||||||
my $class = shift;
|
|
||||||
my $id = shift;
|
|
||||||
if ( !defined($id) ) {
|
|
||||||
Fatal('No monitor defined when invoking protocol '.$class);
|
|
||||||
}
|
|
||||||
my $self = {};
|
|
||||||
$self->{name} = $class;
|
|
||||||
$self->{id} = $id;
|
|
||||||
bless($self, $class);
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub DESTROY {
|
|
||||||
}
|
|
||||||
|
|
||||||
sub AUTOLOAD {
|
sub AUTOLOAD {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $class = ref($self);
|
my $class = ref($self);
|
||||||
|
@ -79,24 +172,24 @@ sub AUTOLOAD {
|
||||||
|
|
||||||
sub getKey {
|
sub getKey {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->{id};
|
return $self->{Id};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub open {
|
sub open {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Fatal('No open method defined for protocol '.$self->{name});
|
Fatal('No open method defined for protocol '.$self->{Protocol});
|
||||||
}
|
}
|
||||||
|
|
||||||
sub close {
|
sub close {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
Debug('No close method defined for protocol '.$self->{name});
|
Debug('No close method defined for protocol '.$self->{Protocol});
|
||||||
}
|
}
|
||||||
|
|
||||||
sub loadMonitor {
|
sub loadMonitor {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
if ( !$self->{Monitor} ) {
|
if ( !$self->{Monitor} ) {
|
||||||
if ( !($self->{Monitor} = ZoneMinder::Monitor->find_one(Id=>$self->{id})) ) {
|
if ( !($self->{Monitor} = ZoneMinder::Monitor->find_one(Id=>$self->{MonitorId})) ) {
|
||||||
Fatal('Monitor id '.$self->{id}.' not found');
|
Fatal('Monitor id '.$self->{id}.' not found');
|
||||||
}
|
}
|
||||||
if ( defined($self->{Monitor}->{AutoStopTimeout}) ) {
|
if ( defined($self->{Monitor}->{AutoStopTimeout}) ) {
|
||||||
|
|
|
@ -41,120 +41,133 @@ our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
use ZoneMinder::General qw(:all);
|
||||||
|
|
||||||
use Time::HiRes qw( usleep );
|
use Time::HiRes qw( usleep );
|
||||||
|
use URI::Encode qw(uri_encode);
|
||||||
|
|
||||||
sub open
|
our $REALM = '';
|
||||||
{
|
our $PROTOCOL = 'http://';
|
||||||
my $self = shift;
|
our $USERNAME = 'admin';
|
||||||
|
our $PASSWORD = '';
|
||||||
|
our $ADDRESS = '';
|
||||||
|
our $BASE_URL = '';
|
||||||
|
|
||||||
$self->loadMonitor();
|
sub open {
|
||||||
Debug( "Camera open" );
|
my $self = shift;
|
||||||
use LWP::UserAgent;
|
$self->loadMonitor();
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
|
||||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
|
||||||
|
|
||||||
$self->{state} = 'open';
|
if (($self->{Monitor}->{ControlAddress} =~ /^(?<PROTOCOL>https?:\/\/)?(?<USERNAME>[^:@]+)?:?(?<PASSWORD>[^\/@]+)?@?(?<ADDRESS>.*)$/)) {
|
||||||
|
$PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL};
|
||||||
|
$USERNAME = $+{USERNAME} if $+{USERNAME};
|
||||||
|
$PASSWORD = $+{PASSWORD} if $+{PASSWORD};
|
||||||
|
$ADDRESS = $+{ADDRESS} if $+{ADDRESS};
|
||||||
|
} else {
|
||||||
|
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
|
||||||
|
$ADDRESS = $self->{Monitor}->{ControlAddress};
|
||||||
|
}
|
||||||
|
if ( !($ADDRESS =~ /:/) ) {
|
||||||
|
Error('You generally need to also specify the port. I will append :80');
|
||||||
|
$ADDRESS .= ':80';
|
||||||
|
}
|
||||||
|
$BASE_URL = $PROTOCOL.($USERNAME?$USERNAME.':'.$PASSWORD.'@':'').$ADDRESS;
|
||||||
|
|
||||||
|
use LWP::UserAgent;
|
||||||
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
|
$self->{ua}->agent( 'ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION );
|
||||||
|
$self->{state} = 'open';
|
||||||
}
|
}
|
||||||
|
|
||||||
sub close
|
sub close {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
$self->{state} = 'closed';
|
||||||
$self->{state} = 'closed';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub printMsg
|
sub sendCmd {
|
||||||
{
|
my ($self, $cmd, $speedcmd) = @_;
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
Debug( $msg."[".$msg_len."]" );
|
$self->printMsg( $speedcmd, 'Tx' );
|
||||||
|
$self->printMsg( $cmd, 'Tx' );
|
||||||
|
|
||||||
|
my $req = HTTP::Request->new( GET => $BASE_URL."/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd");
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
if (!$res->is_success) {
|
||||||
|
Error('Request failed: '.$res->status_line().' (URI: '.$req->as_string().')');
|
||||||
|
}
|
||||||
|
return $res->is_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub sendCmd
|
sub moveConUp {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my ($self, $cmd, $speedcmd) = @_;
|
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||||
|
$self->sendCmd( 'move=up', $speed );
|
||||||
my $result = undef;
|
|
||||||
|
|
||||||
printMsg( $speedcmd, "Tx" );
|
|
||||||
printMsg( $cmd, "Tx" );
|
|
||||||
|
|
||||||
my $req = HTTP::Request->new( GET => "http://" . $self->{Monitor}->{ControlAddress} . "/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" );
|
|
||||||
my $res = $self->{ua}->request($req);
|
|
||||||
|
|
||||||
if ( $res->is_success )
|
|
||||||
{
|
|
||||||
$result = !undef;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error( "Request failed: '" . $res->status_line() . "' (URI: '" . $req->as_string() . "')" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( $result );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUp
|
sub moveConDown {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my ($self, $params) = @_;
|
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
$self->sendCmd( 'move=down', $speed );
|
||||||
Debug( "Move Up" );
|
|
||||||
$self->sendCmd( 'move=up', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDown
|
sub moveConLeft {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my ($self, $params) = @_;
|
my $speed = 'speedpan=-' . $params->{panspeed};
|
||||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
$self->sendCmd( 'move=left', $speed );
|
||||||
Debug( "Move Down" );
|
|
||||||
$self->sendCmd( 'move=down', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConLeft
|
sub moveConRight {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my ($self, $params) = @_;
|
my $speed = 'speedpan=' . ($params->{panspeed} - 6);
|
||||||
my $speed = 'speedpan=-' . $params->{panspeed};
|
$self->sendCmd( 'move=right', $speed );
|
||||||
Debug( "Move Left" );
|
|
||||||
$self->sendCmd( 'move=left', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConRight
|
sub moveStop {
|
||||||
{
|
my $self = shift;
|
||||||
my ($self, $params) = @_;
|
Debug( "Move Stop: not implemented" );
|
||||||
my $speed = 'speedpan=' . ($params->{panspeed} - 6);
|
# not implemented
|
||||||
Debug( "Move Right" );
|
|
||||||
$self->sendCmd( 'move=right', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveStop
|
sub zoomConTele {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my $self = shift;
|
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||||
Debug( "Move Stop" );
|
$self->sendCmd( 'zoom=tele', $speed );
|
||||||
# not implemented
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomConTele
|
sub zoomConWide {
|
||||||
{
|
my ($self, $params) = @_;
|
||||||
my ($self, $params) = @_;
|
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
$self->sendCmd( 'zoom=wide', $speed );
|
||||||
Debug( "Zoom In" );
|
|
||||||
$self->sendCmd( 'zoom=tele', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomConWide
|
sub reset {
|
||||||
{
|
my $self = shift;
|
||||||
my ($self, $params) = @_;
|
$self->sendCmd( 'move=home' );
|
||||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
|
||||||
Debug( "Zoom Out" );
|
|
||||||
$self->sendCmd( 'zoom=wide', $speed );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset
|
sub get_config {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
|
||||||
Debug( "Camera Reset" );
|
my $url = $BASE_URL.'/cgi-bin/admin/lsctrl.cgi?cmd=queryStatus&retType=javascript';
|
||||||
$self->sendCmd( 'move=home' );
|
my $req = new HTTP::Request(GET => $url);
|
||||||
|
my $response = $self->{ua}->request($req);
|
||||||
|
if ( $response->is_success() ) {
|
||||||
|
my $resp = $response->decoded_content;
|
||||||
|
return ZoneMinder::General::parseNameEqualsValueToHash($resp);
|
||||||
|
}
|
||||||
|
Warn("Failed to get config from $url: " . $response->status_line());
|
||||||
|
return;
|
||||||
|
} # end sub get_config
|
||||||
|
|
||||||
|
sub set_config {
|
||||||
|
my $self = shift;
|
||||||
|
my $diff = shift;
|
||||||
|
|
||||||
|
my $url = $BASE_URL.'/cgi-bin/'.$USERNAME.'/setparam.cgi?'.
|
||||||
|
join('&', map { $_.'='.uri_encode($$diff{$_}) } keys %$diff);
|
||||||
|
my $response = $self->{ua}->get($url);
|
||||||
|
Debug($response->content);
|
||||||
|
return $response->is_success();
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -584,6 +584,7 @@ sub DiskSpace {
|
||||||
return $_[0]{DiskSpace};
|
return $_[0]{DiskSpace};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Icon: I removed the locking from this. So we now have an assumption that the Event object is up to date.
|
||||||
sub CopyTo {
|
sub CopyTo {
|
||||||
my ( $self, $NewStorage ) = @_;
|
my ( $self, $NewStorage ) = @_;
|
||||||
|
|
||||||
|
@ -614,16 +615,12 @@ sub CopyTo {
|
||||||
Debug("$NewPath is good");
|
Debug("$NewPath is good");
|
||||||
}
|
}
|
||||||
|
|
||||||
$ZoneMinder::Database::dbh->begin_work();
|
|
||||||
$self->lock_and_load();
|
|
||||||
# data is reloaded, so need to check that the move hasn't already happened.
|
# data is reloaded, so need to check that the move hasn't already happened.
|
||||||
if ( $$self{StorageId} == $$NewStorage{Id} ) {
|
if ( $$self{StorageId} == $$NewStorage{Id} ) {
|
||||||
$ZoneMinder::Database::dbh->commit();
|
|
||||||
return 'Event has already been moved by someone else.';
|
return 'Event has already been moved by someone else.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $$OldStorage{Id} != $$self{StorageId} ) {
|
if ( $$OldStorage{Id} != $$self{StorageId} ) {
|
||||||
$ZoneMinder::Database::dbh->commit();
|
|
||||||
return 'Old Storage path changed, Event has moved somewhere else.';
|
return 'Old Storage path changed, Event has moved somewhere else.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,39 +658,21 @@ sub CopyTo {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $event_path = $subpath.$self->RelativePath();
|
my $event_path = $subpath.$self->RelativePath();
|
||||||
if ( 0 ) { # Not neccessary
|
|
||||||
Debug("Making directory $event_path/");
|
|
||||||
if ( !$bucket->add_key($event_path.'/', '') ) {
|
|
||||||
Warning("Unable to add key for $event_path/ :". $s3->err . ': '. $s3->errstr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my @files = glob("$OldPath/*");
|
my @files = glob("$OldPath/*");
|
||||||
Debug("Files to move @files");
|
Debug("Files to move @files");
|
||||||
foreach my $file ( @files ) {
|
foreach my $file (@files) {
|
||||||
next if $file =~ /^\./;
|
next if $file =~ /^\./;
|
||||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||||
my $starttime = [gettimeofday];
|
my $starttime = [gettimeofday];
|
||||||
Debug("Moving file $file to $NewPath");
|
Debug("Moving file $file to $NewPath");
|
||||||
my $size = -s $file;
|
my $size = -s $file;
|
||||||
if ( ! $size ) {
|
if (!$size) {
|
||||||
Info('Not moving file with 0 size');
|
Info('Not moving file with 0 size');
|
||||||
}
|
}
|
||||||
if ( 0 ) {
|
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||||
my $file_contents = File::Slurp::read_file($file);
|
if (!$bucket->add_key_filename($filename, $file)) {
|
||||||
if ( ! $file_contents ) {
|
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
||||||
die 'Loaded empty file, but it had a size. Giving up';
|
|
||||||
}
|
|
||||||
|
|
||||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
|
||||||
if ( ! $bucket->add_key($filename, $file_contents) ) {
|
|
||||||
die "Unable to add key for $filename : ".$s3->err . ': ' . $s3->errstr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
|
||||||
if ( ! $bucket->add_key_filename($filename, $file) ) {
|
|
||||||
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $duration = tv_interval($starttime);
|
my $duration = tv_interval($starttime);
|
||||||
|
@ -704,16 +683,15 @@ sub CopyTo {
|
||||||
};
|
};
|
||||||
Error($@) if $@;
|
Error($@) if $@;
|
||||||
} else {
|
} else {
|
||||||
Error("Unable to parse S3 Url into it's component parts.");
|
Error('Unable to parse S3 Url into it\'s component parts.');
|
||||||
}
|
}
|
||||||
#die $@ if $@;
|
|
||||||
} # end if Url
|
} # end if Url
|
||||||
} # end if s3
|
} # end if s3
|
||||||
|
|
||||||
my $error = '';
|
my $error = '';
|
||||||
if ( !$moved ) {
|
if (!$moved) {
|
||||||
File::Path::make_path($NewPath, {error => \my $err});
|
File::Path::make_path($NewPath, {error => \my $err});
|
||||||
if ( @$err ) {
|
if (@$err) {
|
||||||
for my $diag (@$err) {
|
for my $diag (@$err) {
|
||||||
my ($file, $message) = %$diag;
|
my ($file, $message) = %$diag;
|
||||||
next if $message eq 'File exists';
|
next if $message eq 'File exists';
|
||||||
|
@ -724,23 +702,16 @@ sub CopyTo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( $error ) {
|
return $error if $error;
|
||||||
$ZoneMinder::Database::dbh->commit();
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
my @files = glob("$OldPath/*");
|
my @files = glob("$OldPath/*");
|
||||||
if ( ! @files ) {
|
return 'No files to move.' if !@files;
|
||||||
$ZoneMinder::Database::dbh->commit();
|
|
||||||
return 'No files to move.';
|
|
||||||
}
|
|
||||||
|
|
||||||
for my $file (@files) {
|
for my $file (@files) {
|
||||||
next if $file =~ /^\./;
|
next if $file =~ /^\./;
|
||||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||||
my $starttime = [gettimeofday];
|
my $starttime = [gettimeofday];
|
||||||
Debug("Moving file $file to $NewPath");
|
|
||||||
my $size = -s $file;
|
my $size = -s $file;
|
||||||
if ( ! File::Copy::copy( $file, $NewPath ) ) {
|
if (!File::Copy::copy($file, $NewPath)) {
|
||||||
$error .= "Copy failed: for $file to $NewPath: $!";
|
$error .= "Copy failed: for $file to $NewPath: $!";
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
@ -749,20 +720,21 @@ sub CopyTo {
|
||||||
} # end foreach file.
|
} # end foreach file.
|
||||||
} # end if ! moved
|
} # end if ! moved
|
||||||
|
|
||||||
if ( $error ) {
|
return $error if $error;
|
||||||
$ZoneMinder::Database::dbh->commit();
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
} # end sub CopyTo
|
} # end sub CopyTo
|
||||||
|
|
||||||
sub MoveTo {
|
sub MoveTo {
|
||||||
my ( $self, $NewStorage ) = @_;
|
my ($self, $NewStorage) = @_;
|
||||||
|
|
||||||
if ( !$self->canEdit() ) {
|
if (!$self->canEdit()) {
|
||||||
Warning('No permission to move event.');
|
Warning('No permission to move event.');
|
||||||
return 'No permission to move event.';
|
return 'No permission to move event.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
||||||
|
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
|
||||||
|
$self->lock_and_load(); # The fact that we are in a transaction might not imply locking
|
||||||
|
|
||||||
my $OldStorage = $self->Storage(undef);
|
my $OldStorage = $self->Storage(undef);
|
||||||
|
|
||||||
my $error = $self->CopyTo($NewStorage);
|
my $error = $self->CopyTo($NewStorage);
|
||||||
|
@ -772,11 +744,11 @@ sub MoveTo {
|
||||||
$$self{StorageId} = $$NewStorage{Id};
|
$$self{StorageId} = $$NewStorage{Id};
|
||||||
$self->Storage($NewStorage);
|
$self->Storage($NewStorage);
|
||||||
$error .= $self->save();
|
$error .= $self->save();
|
||||||
if ( $error ) {
|
if ($error) {
|
||||||
$ZoneMinder::Database::dbh->commit();
|
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||||
return $error;
|
return $error;
|
||||||
}
|
}
|
||||||
$ZoneMinder::Database::dbh->commit();
|
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||||
$self->delete_files($OldStorage);
|
$self->delete_files($OldStorage);
|
||||||
return $error;
|
return $error;
|
||||||
} # end sub MoveTo
|
} # end sub MoveTo
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder Event_Summary Module
|
||||||
|
# Copyright (C) 2020 ZoneMinder
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# This module contains the common definitions and functions used by the rest
|
||||||
|
# of the ZoneMinder scripts
|
||||||
|
#
|
||||||
|
package ZoneMinder::Event_Summary;
|
||||||
|
|
||||||
|
use 5.006;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
require ZoneMinder::Base;
|
||||||
|
require ZoneMinder::Object;
|
||||||
|
|
||||||
|
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
|
use parent qw(ZoneMinder::Object);
|
||||||
|
|
||||||
|
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||||
|
$table = 'Event_Summaries';
|
||||||
|
$serial = $primary_key = 'MonitorId';
|
||||||
|
%fields = map { $_ => $_ } qw(
|
||||||
|
MonitorId
|
||||||
|
TotalEvents
|
||||||
|
TotalEventDiskSpace
|
||||||
|
HourEvents
|
||||||
|
HourEventDiskSpace
|
||||||
|
DayEvents
|
||||||
|
DayEventDiskSpace
|
||||||
|
WeekEvents
|
||||||
|
WeekEventDiskSpace
|
||||||
|
MonthEvents
|
||||||
|
MonthEventDiskSpace
|
||||||
|
ArchivedEvents
|
||||||
|
ArchivedEventDiskSpace
|
||||||
|
);
|
||||||
|
|
||||||
|
%defaults = (
|
||||||
|
TotalEvents => undef,
|
||||||
|
TotalEventDiskSpace => undef,
|
||||||
|
HourEvents => undef,
|
||||||
|
HourEventDiskSpace => undef,
|
||||||
|
DayEvents => undef,
|
||||||
|
DayEventDiskSpace => undef,
|
||||||
|
WeekEvents => undef,
|
||||||
|
WeekEventDiskSpace => undef,
|
||||||
|
MonthEvents => undef,
|
||||||
|
MonthEventDiskSpace => undef,
|
||||||
|
ArchivedEvents => undef,
|
||||||
|
ArchivedEventDiskSpace => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub Monitor {
|
||||||
|
return new ZoneMinder::Monitor( $_[0]{MonitorId} );
|
||||||
|
} # end sub Monitor
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
ZoneMinder::Event_Summary - Perl Class for Event Summaries
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use ZoneMinder::Event_Summary;
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
Copyright (C) 2001-2017 ZoneMinder LLC
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the same terms as Perl itself, either Perl version 5.8.3 or,
|
||||||
|
at your option, any later version of Perl 5 you may have available.
|
||||||
|
|
||||||
|
|
||||||
|
=cut
|
|
@ -31,6 +31,8 @@ our %EXPORT_TAGS = (
|
||||||
systemStatus
|
systemStatus
|
||||||
packageControl
|
packageControl
|
||||||
daemonControl
|
daemonControl
|
||||||
|
parseNameEqualsValueToHash
|
||||||
|
hash_diff
|
||||||
) ]
|
) ]
|
||||||
);
|
);
|
||||||
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
|
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
|
||||||
|
@ -534,6 +536,42 @@ sub jsonDecode {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub parseNameEqualsValueToHash {
|
||||||
|
my %settings;
|
||||||
|
foreach my $line ( split ( /\r?\n/, $_[0] ) ) {
|
||||||
|
next if ! $line;
|
||||||
|
next if ! ( $line =~ /=/ );
|
||||||
|
my ($name, $value ) = split('=', $line);
|
||||||
|
$value =~ s/^'//;
|
||||||
|
$value =~ s/'$//;
|
||||||
|
$settings{$name} = defined $value ? $value : '';
|
||||||
|
}
|
||||||
|
return %settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hash_diff {
|
||||||
|
# assumes keys of second hash are all in the first hash
|
||||||
|
my ( $settings, $defaults ) = @_;
|
||||||
|
my %updates;
|
||||||
|
|
||||||
|
foreach my $setting ( keys %{$settings} ) {
|
||||||
|
next if ! exists $$defaults{$setting};
|
||||||
|
if (
|
||||||
|
($$settings{$setting} and ! $$defaults{$setting})
|
||||||
|
or
|
||||||
|
(!$$settings{$setting} and $$defaults{$setting})
|
||||||
|
or
|
||||||
|
(
|
||||||
|
($$settings{$setting} and $$defaults{$setting} and (
|
||||||
|
$$settings{$setting} ne $$defaults{$setting}))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$updates{$setting} = $$defaults{$setting};
|
||||||
|
}
|
||||||
|
} # end foreach setting
|
||||||
|
return %updates;
|
||||||
|
}
|
||||||
|
|
||||||
sub packageControl {
|
sub packageControl {
|
||||||
my $command = shift;
|
my $command = shift;
|
||||||
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
|
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
|
||||||
|
@ -598,6 +636,8 @@ of the ZoneMinder scripts
|
||||||
packageControl
|
packageControl
|
||||||
daemonControl
|
daemonControl
|
||||||
systemStatus
|
systemStatus
|
||||||
|
parseNameEqualsValueToHash
|
||||||
|
hash_diff
|
||||||
) ]
|
) ]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,9 @@ require ZoneMinder::Storage;
|
||||||
require ZoneMinder::Server;
|
require ZoneMinder::Server;
|
||||||
require ZoneMinder::Memory;
|
require ZoneMinder::Memory;
|
||||||
require ZoneMinder::Monitor_Status;
|
require ZoneMinder::Monitor_Status;
|
||||||
|
require ZoneMinder::Event_Summary;
|
||||||
require ZoneMinder::Zone;
|
require ZoneMinder::Zone;
|
||||||
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
|
||||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
use parent qw(ZoneMinder::Object);
|
use parent qw(ZoneMinder::Object);
|
||||||
|
@ -266,6 +268,15 @@ sub Status {
|
||||||
return $$self{Status};
|
return $$self{Status};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub Event_Summary {
|
||||||
|
my $self = shift;
|
||||||
|
$$self{Event_Summary} = shift if @_;
|
||||||
|
if ( ! $$self{Event_Summary} ) {
|
||||||
|
$$self{Event_Summary} = ZoneMinder::Event_Summary->find_one(MonitorId=>$$self{Id});
|
||||||
|
}
|
||||||
|
return $$self{Event_Summary};
|
||||||
|
}
|
||||||
|
|
||||||
sub connect {
|
sub connect {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return ZoneMinder::Memory::zmMemVerify($self);
|
return ZoneMinder::Memory::zmMemVerify($self);
|
||||||
|
@ -313,6 +324,25 @@ sub resumeMotionDetection {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub Control {
|
||||||
|
my $self = shift;
|
||||||
|
if ( ! exists $$self{Control}) {
|
||||||
|
require ZoneMinder::Control;
|
||||||
|
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
||||||
|
if ($Control) {
|
||||||
|
require Module::Load::Conditional;
|
||||||
|
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$$Control{Protocol} => undef})) {
|
||||||
|
Error("Can't load ZoneMinder::Control::$$Control{Protocol}\n$Module::Load::Conditional::ERROR");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
bless $Control, 'ZoneMinder::Control::'.$$Control{Protocol};
|
||||||
|
$$Control{MonitorId} = $$self{Id};
|
||||||
|
$$self{Control} = $Control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $$self{Control};
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,6 @@ $serial = $primary_key = 'MonitorId';
|
||||||
CaptureFPS
|
CaptureFPS
|
||||||
AnalysisFPS
|
AnalysisFPS
|
||||||
CaptureBandwidth
|
CaptureBandwidth
|
||||||
TotalEvents
|
|
||||||
TotalEventDiskSpace
|
|
||||||
HourEvents
|
|
||||||
HourEventDiskSpace
|
|
||||||
DayEvents
|
|
||||||
DayEventDiskSpace
|
|
||||||
WeekEvents
|
|
||||||
WeekEventDiskSpace
|
|
||||||
MonthEvents
|
|
||||||
MonthEventDiskSpace
|
|
||||||
ArchivedEvents
|
|
||||||
ArchivedEventDiskSpace
|
|
||||||
);
|
);
|
||||||
|
|
||||||
%defaults = (
|
%defaults = (
|
||||||
|
@ -62,18 +50,6 @@ $serial = $primary_key = 'MonitorId';
|
||||||
CaptureFPS => undef,
|
CaptureFPS => undef,
|
||||||
AnalysisFPS => undef,
|
AnalysisFPS => undef,
|
||||||
CaptureBandwidth => undef,
|
CaptureBandwidth => undef,
|
||||||
TotalEvents => undef,
|
|
||||||
TotalEventDiskSpace => undef,
|
|
||||||
HourEvents => undef,
|
|
||||||
HourEventDiskSpace => undef,
|
|
||||||
DayEvents => undef,
|
|
||||||
DayEventDiskSpace => undef,
|
|
||||||
WeekEvents => undef,
|
|
||||||
WeekEventDiskSpace => undef,
|
|
||||||
MonthEvents => undef,
|
|
||||||
MonthEventDiskSpace => undef,
|
|
||||||
ArchivedEvents => undef,
|
|
||||||
ArchivedEventDiskSpace => undef,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
sub Monitor {
|
sub Monitor {
|
||||||
|
|
|
@ -218,7 +218,7 @@ sub save {
|
||||||
my $serial = eval '$'.$type.'::serial';
|
my $serial = eval '$'.$type.'::serial';
|
||||||
my @identified_by = eval '@'.$type.'::identified_by';
|
my @identified_by = eval '@'.$type.'::identified_by';
|
||||||
|
|
||||||
my $ac = ZoneMinder::Database::start_transaction( $local_dbh );
|
my $ac = ZoneMinder::Database::start_transaction( $local_dbh ) if $local_dbh->{AutoCommit};
|
||||||
if ( ! $serial ) {
|
if ( ! $serial ) {
|
||||||
my $insert = $force_insert;
|
my $insert = $force_insert;
|
||||||
my %serial = eval '%'.$type.'::serial';
|
my %serial = eval '%'.$type.'::serial';
|
||||||
|
@ -234,8 +234,8 @@ $log->debug("No serial") if $debug;
|
||||||
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM `$table` WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM `$table` WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
||||||
$where =~ s/\?/\%s/g;
|
$where =~ s/\?/\%s/g;
|
||||||
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
||||||
$local_dbh->rollback();
|
$local_dbh->rollback() if $ac;
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
return $local_dbh->errstr;
|
return $local_dbh->errstr;
|
||||||
} elsif ( $debug ) {
|
} elsif ( $debug ) {
|
||||||
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
||||||
|
@ -267,8 +267,8 @@ $log->debug("No serial") if $debug;
|
||||||
my $error = $local_dbh->errstr;
|
my $error = $local_dbh->errstr;
|
||||||
$command =~ s/\?/\%s/g;
|
$command =~ s/\?/\%s/g;
|
||||||
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
||||||
$local_dbh->rollback();
|
$local_dbh->rollback() if $ac;
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
return $error;
|
return $error;
|
||||||
} # end if
|
} # end if
|
||||||
if ( $debug or DEBUG_ALL ) {
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
@ -282,8 +282,8 @@ $log->debug("No serial") if $debug;
|
||||||
my $error = $local_dbh->errstr;
|
my $error = $local_dbh->errstr;
|
||||||
$command =~ s/\?/\%s/g;
|
$command =~ s/\?/\%s/g;
|
||||||
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
||||||
$local_dbh->rollback();
|
$local_dbh->rollback() if $ac;
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
return $error;
|
return $error;
|
||||||
} # end if
|
} # end if
|
||||||
if ( $debug or DEBUG_ALL ) {
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
@ -321,8 +321,8 @@ $log->debug("No serial") if $debug;
|
||||||
$command =~ s/\?/\%s/g;
|
$command =~ s/\?/\%s/g;
|
||||||
my $error = $local_dbh->errstr;
|
my $error = $local_dbh->errstr;
|
||||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
||||||
$local_dbh->rollback();
|
$local_dbh->rollback() if $ac;
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
return $error;
|
return $error;
|
||||||
} # end if
|
} # end if
|
||||||
if ( $debug or DEBUG_ALL ) {
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
@ -340,8 +340,8 @@ $log->debug("No serial") if $debug;
|
||||||
my $error = $local_dbh->errstr;
|
my $error = $local_dbh->errstr;
|
||||||
$command =~ s/\?/\%s/g;
|
$command =~ s/\?/\%s/g;
|
||||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
||||||
$local_dbh->rollback();
|
$local_dbh->rollback() if $ac;
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
return $error;
|
return $error;
|
||||||
} # end if
|
} # end if
|
||||||
if ( $debug or DEBUG_ALL ) {
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
@ -350,7 +350,7 @@ $log->debug("No serial") if $debug;
|
||||||
} # end if
|
} # end if
|
||||||
} # end if
|
} # end if
|
||||||
} # end if
|
} # end if
|
||||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||||
#$self->load();
|
#$self->load();
|
||||||
#if ( $$fields{id} ) {
|
#if ( $$fields{id} ) {
|
||||||
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
|
||||||
use POSIX qw/strftime EPIPE EINTR/;
|
use POSIX qw/strftime EPIPE EINTR/;
|
||||||
use Socket;
|
use Socket;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Module::Load::Conditional qw{can_load};
|
|
||||||
|
|
||||||
use constant MAX_CONNECT_DELAY => 15;
|
use constant MAX_CONNECT_DELAY => 15;
|
||||||
use constant MAX_COMMAND_WAIT => 1800;
|
use constant MAX_COMMAND_WAIT => 1800;
|
||||||
|
@ -102,40 +101,21 @@ if ($options{command}) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
# The server isn't there
|
# The server isn't there
|
||||||
my $monitor = zmDbGetMonitorAndControl($id);
|
require ZoneMinder::Monitor;
|
||||||
|
|
||||||
|
my $monitor = ZoneMinder::Monitor->find_one(Id=>$id);
|
||||||
Fatal("Unable to load control data for monitor $id") if !$monitor;
|
Fatal("Unable to load control data for monitor $id") if !$monitor;
|
||||||
|
|
||||||
my $protocol = $monitor->{Protocol};
|
my $control = $monitor->Control();
|
||||||
|
|
||||||
|
my $protocol = $control->{Protocol};
|
||||||
if (!$protocol) {
|
if (!$protocol) {
|
||||||
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-x $protocol) {
|
|
||||||
# Protocol is actually a script!
|
|
||||||
# Holdover from previous versions
|
|
||||||
my $command .= $protocol.' '.$arg_string;
|
|
||||||
Debug($command);
|
|
||||||
|
|
||||||
my $output = qx($command);
|
|
||||||
my $status = $? >> 8;
|
|
||||||
if ($status || logDebugging()) {
|
|
||||||
chomp($output);
|
|
||||||
Debug("Output: $output");
|
|
||||||
}
|
|
||||||
if ($status) {
|
|
||||||
Error("Command '$command' exited with status: $status");
|
|
||||||
exit($status);
|
|
||||||
}
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Info("Starting control server $id/$protocol");
|
Info("Starting control server $id/$protocol");
|
||||||
close(CLIENT);
|
close(CLIENT);
|
||||||
|
|
||||||
if (!can_load(modules => {'ZoneMinder::Control::'.$protocol => undef})) {
|
|
||||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
|
||||||
}
|
|
||||||
|
|
||||||
my $zm_terminate = 0;
|
my $zm_terminate = 0;
|
||||||
sub TermHandler {
|
sub TermHandler {
|
||||||
Info('Received TERM, exiting');
|
Info('Received TERM, exiting');
|
||||||
|
@ -150,7 +130,6 @@ if ($options{command}) {
|
||||||
|
|
||||||
$0 = $0.' --id '.$id;
|
$0 = $0.' --id '.$id;
|
||||||
|
|
||||||
my $control = ('ZoneMinder::Control::'.$protocol)->new($id);
|
|
||||||
my $control_key = $control->getKey();
|
my $control_key = $control->getKey();
|
||||||
$control->loadMonitor();
|
$control->loadMonitor();
|
||||||
|
|
||||||
|
|
|
@ -665,10 +665,10 @@ sub substituteTags {
|
||||||
# We have a filter and an event, do we need any more
|
# We have a filter and an event, do we need any more
|
||||||
# monitor information?
|
# monitor information?
|
||||||
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/;
|
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||||
my $need_status = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
my $need_summary = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||||
|
|
||||||
my $Monitor = $Event->Monitor() if $need_monitor;
|
my $Monitor = $Event->Monitor() if $need_monitor;
|
||||||
my $Status = $Monitor->Status() if $need_status;
|
my $Summary = $Monitor->Event_Summary() if $need_summary;
|
||||||
|
|
||||||
# Do we need the image information too?
|
# Do we need the image information too?
|
||||||
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/;
|
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/;
|
||||||
|
@ -692,19 +692,19 @@ sub substituteTags {
|
||||||
}
|
}
|
||||||
$rows ++;
|
$rows ++;
|
||||||
}
|
}
|
||||||
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alaarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
my $url = $Config{ZM_URL};
|
my $url = $Config{ZM_URL};
|
||||||
$text =~ s/%ZP%/$url/g;
|
$text =~ s/%ZP%/$url/g;
|
||||||
$text =~ s/%MN%/$Monitor->{Name}/g;
|
$text =~ s/%MN%/$Monitor->{Name}/g;
|
||||||
$text =~ s/%MET%/$Status->{TotalEvents}/g;
|
$text =~ s/%MET%/$Summary->{TotalEvents}/g;
|
||||||
$text =~ s/%MEH%/$Status->{HourEvents}/g;
|
$text =~ s/%MEH%/$Summary->{HourEvents}/g;
|
||||||
$text =~ s/%MED%/$Status->{DayEvents}/g;
|
$text =~ s/%MED%/$Summary->{DayEvents}/g;
|
||||||
$text =~ s/%MEW%/$Status->{WeekEvents}/g;
|
$text =~ s/%MEW%/$Summary->{WeekEvents}/g;
|
||||||
$text =~ s/%MEM%/$Status->{MonthEvents}/g;
|
$text =~ s/%MEM%/$Summary->{MonthEvents}/g;
|
||||||
$text =~ s/%MEA%/$Status->{ArchivedEvents}/g;
|
$text =~ s/%MEA%/$Summary->{ArchivedEvents}/g;
|
||||||
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
|
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
|
||||||
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
|
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
|
||||||
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
|
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
|
||||||
|
|
|
@ -251,6 +251,13 @@ void zmDbQueue::process() {
|
||||||
mCondition.wait(lock);
|
mCondition.wait(lock);
|
||||||
}
|
}
|
||||||
while (!mQueue.empty()) {
|
while (!mQueue.empty()) {
|
||||||
|
if (mQueue.size() > 10) {
|
||||||
|
Logger *log = Logger::fetch();
|
||||||
|
Logger::Level db_level = log->databaseLevel();
|
||||||
|
log->databaseLevel(Logger::NOLOG);
|
||||||
|
Warning("db queue size has grown larger than 10 entries");
|
||||||
|
log->databaseLevel(db_level);
|
||||||
|
}
|
||||||
std::string sql = mQueue.front();
|
std::string sql = mQueue.front();
|
||||||
mQueue.pop();
|
mQueue.pop();
|
||||||
// My idea for leaving the locking around each sql statement is to allow
|
// My idea for leaving the locking around each sql statement is to allow
|
||||||
|
|
|
@ -86,6 +86,7 @@ std::string load_monitor_sql =
|
||||||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
||||||
|
|
||||||
std::string CameraType_Strings[] = {
|
std::string CameraType_Strings[] = {
|
||||||
|
"Unknown",
|
||||||
"Local",
|
"Local",
|
||||||
"Remote",
|
"Remote",
|
||||||
"File",
|
"File",
|
||||||
|
@ -93,10 +94,21 @@ std::string CameraType_Strings[] = {
|
||||||
"LibVLC",
|
"LibVLC",
|
||||||
"NVSOCKET",
|
"NVSOCKET",
|
||||||
"CURL",
|
"CURL",
|
||||||
"VNC",
|
"VNC"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string Function_Strings[] = {
|
||||||
|
"Unknown",
|
||||||
|
"None",
|
||||||
|
"Monitor",
|
||||||
|
"Modect",
|
||||||
|
"Record",
|
||||||
|
"Mocord",
|
||||||
|
"Nodect"
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string State_Strings[] = {
|
std::string State_Strings[] = {
|
||||||
|
"Unknown",
|
||||||
"IDLE",
|
"IDLE",
|
||||||
"PREALARM",
|
"PREALARM",
|
||||||
"ALARM",
|
"ALARM",
|
||||||
|
@ -474,16 +486,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
function = (Function)atoi(dbrow[col]); col++;
|
function = (Function)atoi(dbrow[col]); col++;
|
||||||
enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||||
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||||
decoding_enabled = !(
|
// See below after save_jpegs for a recalculation of decoding_enabled
|
||||||
( function == RECORD or function == NODECT )
|
|
||||||
and
|
|
||||||
( savejpegs == 0 )
|
|
||||||
and
|
|
||||||
( videowriter == PASSTHROUGH )
|
|
||||||
and
|
|
||||||
!decoding_enabled
|
|
||||||
);
|
|
||||||
Debug(1, "Decoding enabled: %d", decoding_enabled);
|
|
||||||
|
|
||||||
ReloadLinkedMonitors(dbrow[col]); col++;
|
ReloadLinkedMonitors(dbrow[col]); col++;
|
||||||
|
|
||||||
|
@ -552,6 +555,17 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
videowriter = (VideoWriter)atoi(dbrow[col]); col++;
|
videowriter = (VideoWriter)atoi(dbrow[col]); col++;
|
||||||
encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
|
encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
|
||||||
|
|
||||||
|
decoding_enabled = !(
|
||||||
|
( function == RECORD or function == NODECT )
|
||||||
|
and
|
||||||
|
( savejpegs == 0 )
|
||||||
|
and
|
||||||
|
( videowriter == PASSTHROUGH )
|
||||||
|
and
|
||||||
|
!decoding_enabled
|
||||||
|
);
|
||||||
|
Debug(3, "Decoding enabled: %d function %d %s savejpegs %d videowriter %d", decoding_enabled, function, Function_Strings[function].c_str(), savejpegs, videowriter);
|
||||||
|
|
||||||
/*"`OutputCodec`, `Encoder`, `OutputContainer`, " */
|
/*"`OutputCodec`, `Encoder`, `OutputContainer`, " */
|
||||||
output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||||
encoder = dbrow[col] ? dbrow[col] : ""; col++;
|
encoder = dbrow[col] ? dbrow[col] : ""; col++;
|
||||||
|
@ -2040,8 +2054,7 @@ bool Monitor::Analyse() {
|
||||||
} // end if ! event
|
} // end if ! event
|
||||||
} // end if RECORDING
|
} // end if RECORDING
|
||||||
|
|
||||||
if (score) {
|
if (score and (function == MODECT or function == NODECT)) {
|
||||||
|
|
||||||
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
|
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
|
||||||
// If we should end then previous continuous event and start a new non-continuous event
|
// If we should end then previous continuous event and start a new non-continuous event
|
||||||
if (event && event->Frames()
|
if (event && event->Frames()
|
||||||
|
|
|
@ -151,6 +151,7 @@ bool VideoStore::open() {
|
||||||
Debug(3, "Encoder Option %s=%s", e->key, e->value);
|
Debug(3, "Encoder Option %s=%s", e->key, e->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
av_dict_free(&opts);
|
||||||
|
|
||||||
if (video_in_stream) {
|
if (video_in_stream) {
|
||||||
zm_dump_codecpar(video_in_stream->codecpar);
|
zm_dump_codecpar(video_in_stream->codecpar);
|
||||||
|
@ -184,6 +185,7 @@ bool VideoStore::open() {
|
||||||
}
|
}
|
||||||
} // end if orientation
|
} // end if orientation
|
||||||
|
|
||||||
|
av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||||
if (av_dict_get(opts, "new_extradata", nullptr, AV_DICT_MATCH_CASE)) {
|
if (av_dict_get(opts, "new_extradata", nullptr, AV_DICT_MATCH_CASE)) {
|
||||||
av_dict_set(&opts, "new_extradata", nullptr, 0);
|
av_dict_set(&opts, "new_extradata", nullptr, 0);
|
||||||
// Special flag to tell us to open a codec to get new extraflags to fix weird h265
|
// Special flag to tell us to open a codec to get new extraflags to fix weird h265
|
||||||
|
@ -224,14 +226,13 @@ bool VideoStore::open() {
|
||||||
);
|
);
|
||||||
video_out_codec = nullptr;
|
video_out_codec = nullptr;
|
||||||
}
|
}
|
||||||
av_dict_free(&opts);
|
|
||||||
av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
|
||||||
} // end if video_out_codec
|
} // end if video_out_codec
|
||||||
|
|
||||||
ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx);
|
ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
Error("Could not initialize stream parameteres");
|
Error("Could not initialize stream parameteres");
|
||||||
}
|
}
|
||||||
|
av_dict_free(&opts);
|
||||||
} // end if extradata_entry
|
} // end if extradata_entry
|
||||||
} else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) {
|
} else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) {
|
||||||
int wanted_codec = monitor->OutputCodec();
|
int wanted_codec = monitor->OutputCodec();
|
||||||
|
@ -486,6 +487,7 @@ bool VideoStore::open() {
|
||||||
zm_dump_stream_format(oc, 0, 0, 1);
|
zm_dump_stream_format(oc, 0, 0, 1);
|
||||||
if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1);
|
if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1);
|
||||||
|
|
||||||
|
av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||||
const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE);
|
const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE);
|
||||||
if (!movflags_entry) {
|
if (!movflags_entry) {
|
||||||
Debug(1, "setting movflags to frag_keyframe+empty_moov");
|
Debug(1, "setting movflags to frag_keyframe+empty_moov");
|
||||||
|
|
|
@ -6,28 +6,30 @@ $data = array();
|
||||||
// INITIALIZE AND CHECK SANITY
|
// INITIALIZE AND CHECK SANITY
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
|
if (!canView('Events'))
|
||||||
|
$message = 'Insufficient permissions for user '.$user['Username'].'<br/>';
|
||||||
|
|
||||||
if ( empty($_REQUEST['task']) ) {
|
if (empty($_REQUEST['task'])) {
|
||||||
$message = 'Must specify a task';
|
$message = 'Must specify a task<br/>';
|
||||||
} else {
|
} else {
|
||||||
$task = $_REQUEST['task'];
|
$task = $_REQUEST['task'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( empty($_REQUEST['eids']) ) {
|
if (empty($_REQUEST['eids'])) {
|
||||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No event id(s) supplied';
|
if (isset($_REQUEST['task']) && $_REQUEST['task'] != 'query')
|
||||||
|
$message = 'No event id(s) supplied<br/>';
|
||||||
} else {
|
} else {
|
||||||
$eids = $_REQUEST['eids'];
|
$eids = $_REQUEST['eids'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $message ) {
|
if ($message) {
|
||||||
ajaxError($message);
|
ajaxError($message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once('includes/Filter.php');
|
require_once('includes/Filter.php');
|
||||||
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter();
|
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter();
|
||||||
if ( $user['MonitorIds'] ) {
|
if ($user['MonitorIds']) {
|
||||||
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +41,19 @@ $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
||||||
$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
|
$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
|
||||||
|
|
||||||
// Order specifies the sort direction, either asc or desc
|
// Order specifies the sort direction, either asc or desc
|
||||||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
$order = $filter->sort_asc() ? 'ASC' : 'DESC';
|
||||||
|
if (isset($_REQUEST['order'])) {
|
||||||
|
if (strtolower($_REQUEST['order']) == 'asc') {
|
||||||
|
$order = 'ASC';
|
||||||
|
} else if (strtolower($_REQUEST['order']) == 'desc') {
|
||||||
|
$order = 'DESC';
|
||||||
|
} else {
|
||||||
|
Warning("Invalid value for order " . $_REQUEST['order']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sort specifies the name of the column to sort on
|
// Sort specifies the name of the column to sort on
|
||||||
$sort = 'StartDateTime';
|
$sort = $filter->sort_field();
|
||||||
if (isset($_REQUEST['sort'])) {
|
if (isset($_REQUEST['sort'])) {
|
||||||
$sort = $_REQUEST['sort'];
|
$sort = $_REQUEST['sort'];
|
||||||
if ($sort == 'EndDateTime') {
|
if ($sort == 'EndDateTime') {
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
|
||||||
ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file
|
ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file
|
||||||
|
|
||||||
|
### Font Awesome icons
|
||||||
|
|
||||||
|
Origin: http://fontawesome.io
|
||||||
|
|
||||||
|
License: Font: SIL OFL 1.1, CSS: MIT License (http://fontawesome.io/license)
|
||||||
|
|
||||||
### Material Design icons
|
### Material Design icons
|
||||||
|
|
||||||
Origin: https://github.com/google/material-design-icons
|
Origin: https://github.com/google/material-design-icons
|
||||||
|
|
|
@ -60,6 +60,9 @@ class Filter extends ZM_Object {
|
||||||
foreach ( $this->FilterTerms() as $term ) {
|
foreach ( $this->FilterTerms() as $term ) {
|
||||||
$this->_querystring .= $term->querystring($objectname, $separator);
|
$this->_querystring .= $term->querystring($objectname, $separator);
|
||||||
} # end foreach term
|
} # 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][limit]').'='.$this->limit();
|
||||||
if ( $this->Id() ) {
|
if ( $this->Id() ) {
|
||||||
$this->_querystring .= $separator.$objectname.urlencode('[Id]').'='.$this->Id();
|
$this->_querystring .= $separator.$objectname.urlencode('[Id]').'='.$this->Id();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,18 +20,18 @@
|
||||||
|
|
||||||
|
|
||||||
// Monitor control actions, require a monitor id and control view permissions for that monitor
|
// Monitor control actions, require a monitor id and control view permissions for that monitor
|
||||||
if ( empty($_REQUEST['mid']) ) {
|
if (empty($_REQUEST['mid'])) {
|
||||||
ZM\Warning('Settings requires a monitor id');
|
ZM\Warning('Settings requires a monitor id');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( ! canView('Control', $_REQUEST['mid']) ) {
|
if (!canView('Control', $_REQUEST['mid'])) {
|
||||||
ZM\Warning('Settings requires the Control permission');
|
ZM\Warning('Settings requires the Control permission');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once('includes/Monitor.php');
|
require_once('includes/Monitor.php');
|
||||||
$mid = validInt($_REQUEST['mid']);
|
$mid = validInt($_REQUEST['mid']);
|
||||||
if ( $action == 'settings' ) {
|
if ($action == 'settings') {
|
||||||
$args = ' -m ' . escapeshellarg($mid);
|
$args = ' -m ' . escapeshellarg($mid);
|
||||||
$args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
|
$args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
|
||||||
$args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
|
$args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
|
||||||
|
@ -45,5 +45,7 @@ if ( $action == 'settings' ) {
|
||||||
dbQuery(
|
dbQuery(
|
||||||
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
|
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
|
||||||
array($brightness, $contrast, $hue, $colour, $mid));
|
array($brightness, $contrast, $hue, $colour, $mid));
|
||||||
|
global $redirect;
|
||||||
|
$redirect = '?view=watch&mid='.$mid;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -50,6 +50,7 @@ select.chosen {
|
||||||
}
|
}
|
||||||
tr td:first-child {
|
tr td:first-child {
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
.OutputContainer {
|
.OutputContainer {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -153,6 +153,7 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
|
||||||
download
|
download
|
||||||
<?php echo $Event->DefaultVideo() ? '' : 'style="display:none;"' ?>
|
<?php echo $Event->DefaultVideo() ? '' : 'style="display:none;"' ?>
|
||||||
><i class="fa fa-download"></i></a>
|
><i class="fa fa-download"></i></a>
|
||||||
|
<button id="videoBtn" class="btn btn-normal" data-toggle="tooltip" data-toggle="tooltip" data-placement="top" title="<?php echo translate('GenerateVideo') ?>"><i class="fa fa-file-video-o"></i></button>
|
||||||
<button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button>
|
<button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button>
|
||||||
<button id="framesBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Frames') ?>" ><i class="fa fa-picture-o"></i></button>
|
<button id="framesBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Frames') ?>" ><i class="fa fa-picture-o"></i></button>
|
||||||
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button>
|
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button>
|
||||||
|
|
|
@ -79,11 +79,14 @@ getBodyTopHTML();
|
||||||
data-cookie-id-table="zmEventsTable"
|
data-cookie-id-table="zmEventsTable"
|
||||||
data-cookie-expire="2y"
|
data-cookie-expire="2y"
|
||||||
data-click-to-select="true"
|
data-click-to-select="true"
|
||||||
data-remember-order="true"
|
data-remember-order="false"
|
||||||
data-show-columns="true"
|
data-show-columns="true"
|
||||||
data-show-export="true"
|
data-show-export="true"
|
||||||
data-uncheckAll="true"
|
data-uncheckAll="true"
|
||||||
data-toolbar="#toolbar"
|
data-toolbar="#toolbar"
|
||||||
|
data-sort-name="<?php echo $filter->sort_field() ?>"
|
||||||
|
data-sort-order="<?php echo $filter->sort_asc() ? 'asc' : 'desc' ?>"
|
||||||
|
data-server-sort="true"
|
||||||
data-show-fullscreen="true"
|
data-show-fullscreen="true"
|
||||||
data-click-to-select="true"
|
data-click-to-select="true"
|
||||||
data-maintain-meta-data="true"
|
data-maintain-meta-data="true"
|
||||||
|
|
|
@ -708,7 +708,7 @@ function renameEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportEvent() {
|
function exportEvent() {
|
||||||
window.location.assign('?view=export&eid='+eventData.Id);
|
window.location.assign('?view=export&eids[]='+eventData.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEventFrames() {
|
function showEventFrames() {
|
||||||
|
@ -767,14 +767,15 @@ function handleClick(event) {
|
||||||
// Manage the DELETE CONFIRMATION modal button
|
// Manage the DELETE CONFIRMATION modal button
|
||||||
function manageDelConfirmModalBtns() {
|
function manageDelConfirmModalBtns() {
|
||||||
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
|
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
|
||||||
if ( !canEdit.Events ) {
|
if (!canEdit.Events) {
|
||||||
enoperm();
|
enoperm();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eventData.Id)
|
$j.getJSON(thisUrl + '?request=event&task=delete&id='+eventData.Id)
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
|
$j('#deleteConfirm').modal('hide');
|
||||||
streamNext(true);
|
streamNext(true);
|
||||||
})
|
})
|
||||||
.fail(logAjaxFail);
|
.fail(logAjaxFail);
|
||||||
|
@ -1015,7 +1016,13 @@ function initPage() {
|
||||||
// Manage the EXPORT button
|
// Manage the EXPORT button
|
||||||
bindButton('#exportBtn', 'click', null, function onExportClick(evt) {
|
bindButton('#exportBtn', 'click', null, function onExportClick(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
window.location.assign('?view=export&eids[]='+eventData.Id);
|
exportEvent();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manage the generateVideo button
|
||||||
|
bindButton('#videoBtn', 'click', null, function onExportClick(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
videoEvent();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Manage the Event STATISTICS Button
|
// Manage the Event STATISTICS Button
|
||||||
|
|
|
@ -945,7 +945,7 @@ function initPage() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only enable the settings button for local cameras
|
// Only enable the settings button for local cameras
|
||||||
settingsBtn.prop('disabled', !(canView.Control && monitorType == 'Local'));
|
settingsBtn.prop('disabled', !(canView.Control && (monitorType == 'Local')));
|
||||||
|
|
||||||
// Init the bootstrap-table
|
// Init the bootstrap-table
|
||||||
if (monitorType != 'WebSite') table.bootstrapTable({icons: icons});
|
if (monitorType != 'WebSite') table.bootstrapTable({icons: icons});
|
||||||
|
|
|
@ -102,7 +102,7 @@ $focusWindow = true;
|
||||||
xhtmlHeaders(__FILE__, translate('Video'));
|
xhtmlHeaders(__FILE__, translate('Video'));
|
||||||
?>
|
?>
|
||||||
<body>
|
<body>
|
||||||
<?php if ( !$popup ) echo getNavBarHTML() ?>
|
<?php echo getNavBarHTML() ?>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<div class="w-100 py-1">
|
<div class="w-100 py-1">
|
||||||
<div class="float-left pl-3">
|
<div class="float-left pl-3">
|
||||||
|
|
Loading…
Reference in New Issue