Merge branch 'master' into improve_filter_emails

This commit is contained in:
Isaac Connor 2020-02-17 16:42:44 -05:00
commit fc8f94c315
28 changed files with 298 additions and 211 deletions

5
db/zm_update-1.34.1.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.34.0 database to 1.34.1
--
-- No changes required
--

5
db/zm_update-1.34.2.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.34.1 database to 1.34.2
--
-- No changes required
--

View File

@ -28,7 +28,7 @@
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.34.0 Version: 1.34.2
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons
@ -416,6 +416,12 @@ EOF
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
%changelog %changelog
* Tue Feb 04 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.2-1
- 1.34.2 Release
* Fri Jan 31 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.1-1
- 1.34.1 Release
* Sat Jan 18 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.0-1 * Sat Jan 18 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.0-1
- 1.34.0 Release - 1.34.0 Release

View File

@ -127,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent
Key lifetime (v2.0) Key lifetime (v2.0)
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_exipres`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs. In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
Understanding access/refresh tokens (v2.0) Understanding access/refresh tokens (v2.0)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -109,7 +109,7 @@ This brings up the new monitor window:
* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone` * In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone`
* In Analytis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs * In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
.. note:: .. note::
Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example) Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example)

View File

@ -76,7 +76,7 @@ my %soap_version_of :ATTR(:default<('1.1')>);
sub service { sub service {
my ($self, $serviceName, $attr) = @_; my ($self, $serviceName, $attr) = @_;
#print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n"; #print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n";
# Please note that the Std::Class::Fast docs say not to use ident. # Please note that the Std::Class::Fast docs say not to use ident.
$services_of{ident $self}{$serviceName}{$attr}; $services_of{ident $self}{$serviceName}{$attr};
} }
@ -114,18 +114,18 @@ sub get_service_urls {
my $result = $self->service('device', 'ep')->GetServices( { my $result = $self->service('device', 'ep')->GetServices( {
IncludeCapability => 'true', # boolean IncludeCapability => 'true', # boolean
},, }
); );
if ( $result ) { if ( $result ) {
foreach my $svc ( @{ $result->get_Service() } ) { foreach my $svc ( @{ $result->get_Service() } ) {
my $short_name = $namespace_map{$svc->get_Namespace()}; my $short_name = $namespace_map{$svc->get_Namespace()};
my $url_svc = $svc->get_XAddr()->get_value(); my $url_svc = $svc->get_XAddr()->get_value();
if(defined $short_name && defined $url_svc) { if ( defined $short_name && defined $url_svc ) {
# print "Got $short_name service\n"; #print "Got $short_name service\n";
$self->set_service($short_name, 'url', $url_svc); $self->set_service($short_name, 'url', $url_svc);
} }
} }
# } else { #} else {
#print "No results from GetServices: $result\n"; #print "No results from GetServices: $result\n";
} }
@ -142,14 +142,14 @@ sub get_service_urls {
if ( my $function = $capabilities->can( "get_$capability" ) ) { if ( my $function = $capabilities->can( "get_$capability" ) ) {
my $Services = $function->( $capabilities ); my $Services = $function->( $capabilities );
if ( !$Services ) { if ( !$Services ) {
print "Nothing returned ffrom get_$capability\n"; #print "Nothing returned from get_$capability\n";
} else { } else {
foreach my $svc ( @{ $Services } ) { foreach my $svc ( @{ $Services } ) {
# The capability versions don't have a namespace, so just lowercase them. # The capability versions don't have a namespace, so just lowercase them.
my $short_name = lc $capability; my $short_name = lc $capability;
my $url_svc = $svc->get_XAddr()->get_value(); my $url_svc = $svc->get_XAddr()->get_value();
if( defined $url_svc) { if ( defined $url_svc ) {
# print "Got $short_name service\n"; #print "Got $short_name service\n";
$self->set_service($short_name, 'url', $url_svc); $self->set_service($short_name, 'url', $url_svc);
} }
} # end foreach svr } # end foreach svr
@ -202,10 +202,10 @@ sub BUILD {
# deserializer_args => { strict => 0 } # deserializer_args => { strict => 0 }
}); });
$services_of{$ident}{'device'} = { url => $url_svc_device, ep => $svc_device }; $services_of{$ident}{device} = { url => $url_svc_device, ep => $svc_device };
# Can't, don't have credentials yet # Can't, don't have credentials yet
#$self->get_service_urls(); # $self->get_service_urls();
} }
sub get_users { sub get_users {
@ -260,7 +260,7 @@ sub set_credentials {
sub create_services { sub create_services {
my ($self) = @_; my ($self) = @_;
#$self->get_service_urls(); $self->get_service_urls();
if ( defined $self->service('media', 'url') ) { if ( defined $self->service('media', 'url') ) {
$self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({ $self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({

View File

@ -785,7 +785,7 @@ our @options = (
}, },
{ {
name => 'ZM_TIMEZONE', name => 'ZM_TIMEZONE',
default => 'UTC', default => '',
description => 'The timezone that php should use.', description => 'The timezone that php should use.',
help => q` help => q`
This should be set equal to the system timezone of the mysql server`, This should be set equal to the system timezone of the mysql server`,

View File

@ -1,16 +1,6 @@
# ========================================================================== # ==========================================================================
# #
# ZoneMinder Amcrest HTTP API Control Protocol Module, 20180214, Rev 3.0 # ZoneMinder Amcrest HTTP API Control Protocol Module
#
# Change Log
#
# Rev 3.0:
# - Fixes incorrect method names
# - Updates control sequences to Amcrest HTTP Protocol API v 2.12
# - Extends control features
#
# Rev 2.0:
# - Fixed installation instructions text, no changes to functionality.
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@ -67,18 +57,20 @@ sub open {
my $password; my $password;
my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice}; my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice};
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) { if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) {
$username = $1; $username = $1;
$password = $2; $password = $2;
$$self{address} = $3; $$self{address} = $3;
$self->{ua}->credentials($$self{address}, $realm, $username, $password);
# Testing seems to show that we need the username/password in each url as well as credentials
$$self{base_url} = 'http://'.$self->{Monitor}->{ControlDevice};
Debug("Using initial credentials for $$self{address}, $realm, $username, $password");
} }
$self->{ua} = LWP::UserAgent->new; # Detect REALM, has to be /cgi-bin/ptz.cgi because just / accepts no auth
$self->{ua}->credentials($$self{address}, $realm, $username, $password); my $res = $self->{ua}->get($$self{base_url}.'/cgi-bin/ptz.cgi');
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
# Detect REALM
my $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi');
if ( $res->is_success ) { if ( $res->is_success ) {
$self->{state} = 'open'; $self->{state} = 'open';
@ -94,21 +86,26 @@ sub open {
if ( $$headers{'www-authenticate'} ) { if ( $$headers{'www-authenticate'} ) {
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ( $tokens =~ /\w+="([^"]+)"/i ) { if ( $tokens =~ /realm="([^"]+)"/i ) {
if ( $realm ne $1 ) { if ( $realm ne $1 ) {
$realm = $1; $realm = $1;
Debug("Changing REALM to $realm"); Debug("Changing REALM to ($realm)");
$self->{ua}->credentials($$self{address}, $realm, $username, $password); $self->{ua}->credentials($$self{address}, $realm, $username, $password);
$res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi');
if ( $res->is_success() ) { if ( $res->is_success() ) {
$self->{state} = 'open'; $self->{state} = 'open';
return; return;
} } elsif ( $res->status_line eq '400 Bad Request' ) {
# In testing, this second request fails with Bad Request, I assume because we didn't actually give it a command.
$self->{state} = 'open';
return;
} else {
Error('Authentication still failed after updating REALM' . $res->status_line); Error('Authentication still failed after updating REALM' . $res->status_line);
$headers = $res->headers(); $headers = $res->headers();
foreach my $k ( keys %$headers ) { foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}"); Debug("Header $k => $$headers{$k}");
} # end foreach } # end foreach
}
} else { } else {
Error('Authentication failed, not a REALM problem'); Error('Authentication failed, not a REALM problem');
} }
@ -118,9 +115,12 @@ sub open {
} else { } else {
Debug('No headers line'); Debug('No headers line');
} # end if headers } # end if headers
} else {
Error("Failed to get $$self{address}/cgi-bin/ptz.cgi ".$res->status_line());
} # end if $res->status_line() eq '401 Unauthorized' } # end if $res->status_line() eq '401 Unauthorized'
$self->{state} = 'open'; $self->{state} = 'closed';
} }
sub close { sub close {
@ -135,16 +135,23 @@ sub sendCmd {
$self->printMsg($cmd, 'Tx'); $self->printMsg($cmd, 'Tx');
my $req = HTTP::Request->new( GET=>"http://$$self{address}/$cmd" ); my $res = $self->{ua}->get("http://$$self{address}/$cmd");
my $res = $self->{ua}->request($req);
if ( $res->is_success ) { if ( $res->is_success ) {
$result = !undef; $result = !undef;
# Command to camera appears successful, write Info item to log # Command to camera appears successful, write Info item to log
Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd);
# TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status.
} else { } else {
Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); # Try again
$res = $self->{ua}->get("http://$$self{address}/$cmd");
if ( $res->is_success ) {
# Command to camera appears successful, write Info item to log
Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd);
} else {
Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd);
$res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd);
}
} }
return $result; return $result;

View File

@ -255,15 +255,15 @@ sub discover {
sub profiles { sub profiles {
my ( $client ) = @_; my ( $client ) = @_;
my $endpoint = $client->get_endpoint('media'); my $media = $client->get_endpoint('media');
if ( ! $endpoint ) { if ( ! $media ) {
print "No media enpoint for client.\n"; print "No media endpoint for client.\n";
return; return;
} }
my $result = $endpoint->GetProfiles( { } ,, ); my $result = $media->GetProfiles( { } ,, );
if ( ! $result ) { if ( ! $result ) {
print "No result from GetProfiles\n"; print "No result from GetProfiles.\n";
return; return;
} }
if ( $verbose ) { if ( $verbose ) {
@ -275,23 +275,18 @@ sub profiles {
foreach my $profile ( @{ $profiles } ) { foreach my $profile ( @{ $profiles } ) {
my $token = $profile->attr()->get_token() ; my $token = $profile->attr()->get_token() ;
my $video_encoder_configuration = $profile->get_VideoEncoderConfiguration(); my $Name = $profile->get_Name();
if ( ! $video_encoder_configuration ) {
print "Unknown profile $token " . $profile->get_Name()."\n"; my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
if ( ! $VideoEncoderConfiguration ) {
print "Unknown profile $token $Name.\n";
next; next;
} }
print $token . ", " .
$profile->get_Name() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Encoding() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Width() . ", " .
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Height() . ", " .
$profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() .
", ";
# Specification gives conflicting values for unicast stream types, try both. # Specification gives conflicting values for unicast stream types, try both.
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri # http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) { foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
$result = $client->get_endpoint('media')->GetStreamUri( { my $StreamUri = $media->GetStreamUri( {
StreamSetup => { # ONVIF::Media::Types::StreamSetup StreamSetup => { # ONVIF::Media::Types::StreamSetup
Stream => $streamtype, # StreamType Stream => $streamtype, # StreamType
Transport => { # ONVIF::Media::Types::Transport Transport => { # ONVIF::Media::Types::Transport
@ -299,21 +294,30 @@ sub profiles {
}, },
}, },
ProfileToken => $token, # ReferenceToken ProfileToken => $token, # ReferenceToken
} ,, ); } );
last if $result; next if ! ( $StreamUri and $StreamUri->can('get_MediaUri') );
} my $MediaUri = $StreamUri->get_MediaUri();
die $result if not $result; next if ! $MediaUri;
# print $result . "\n"; my $Uri = $MediaUri->get_Uri();
next if ! $Uri;
print join(', ', $token,
$Name,
$VideoEncoderConfiguration->get_Encoding(),
$VideoEncoderConfiguration->get_Resolution()->get_Width(),
$VideoEncoderConfiguration->get_Resolution()->get_Height(),
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
$Uri,
) . "\n";
} # end foreach streamtype
print $result->get_MediaUri()->get_Uri() .
"\n";
} # end foreach profile } # end foreach profile
# #
# use message parser without schema validation ??? # use message parser without schema validation ???
# #
} } # end sub profiles
sub move { sub move {
my ($client, $dir) = @_; my ($client, $dir) = @_;
@ -326,13 +330,22 @@ sub move {
sub metadata { sub metadata {
my ( $client ) = @_; my ( $client ) = @_;
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, ); my $media = $client->get_endpoint('media');
die $result if not $result; die 'No media endpoint.' if !$media;
print $result . "\n";
$result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, ); my $result = $media->GetMetadataConfigurations( { } ,, );
die $result if not $result; if ( ! $result ) {
print "No MetaDataConfigurations\n" if $verbose;
} else {
print $result . "\n"; print $result . "\n";
}
$result = $media->GetVideoAnalyticsConfigurations( { } ,, );
if ( ! $result ) {
print "No VideoAnalyticsConfigurations\n" if $verbose;
} else {
print $result . "\n";
}
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, ); # $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
# die $result if not $result; # die $result if not $result;

View File

@ -420,15 +420,15 @@ MAIN: while( $loop ) {
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir"); Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
{ {
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*"); my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . ' entries.' ); Debug('glob("'.$monitor_dir.'/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned '.(scalar @event_dirs).' entries.');
foreach my $event_dir ( @event_dirs ) { foreach my $event_dir ( @event_dirs ) {
if ( ! -d $event_dir ) { if ( ! -d $event_dir ) {
Debug( "$event_dir is not a dir. Skipping" ); Debug("$event_dir is not a dir. Skipping");
next; next;
} }
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/; my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
if ( ! $event_id ) { if ( !$event_id ) {
Debug("Unable to parse date/event_id from $event_dir"); Debug('Unable to parse date/event_id from '.$event_dir);
next; next;
} }
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
@ -438,8 +438,9 @@ MAIN: while( $loop ) {
$Event->MonitorId( $monitor_dir ); $Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() ); $Event->StorageId( $Storage->Id() );
$Event->Path(); $Event->Path();
$Event->age();
Debug("Have event $$Event{Id} at $$Event{Path}"); Debug("Have event $$Event{Id} at $$Event{Path}");
$Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())) ) ); $Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path()))));
} # end foreach event } # end foreach event
} }
@ -466,13 +467,13 @@ MAIN: while( $loop ) {
} # end foreach event } # end foreach event
chdir( $Storage->Path() ); chdir( $Storage->Path() );
} # if USE_DEEP_STORAGE } # if USE_DEEP_STORAGE
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir" ); Debug('Got '.int(keys(%$fs_events)).' filesystem events for monitor '.$monitor_dir);
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir); delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
} # end foreach monitor } # end foreach monitor
if ( $cleaned ) { if ( $cleaned ) {
Debug("First stage cleaning done. Restarting."); Debug('First stage cleaning done. Restarting.');
redo MAIN; redo MAIN;
} }
@ -484,7 +485,7 @@ MAIN: while( $loop ) {
next; next;
} }
my @event_ids = keys %$fs_events; my @event_ids = keys %$fs_events;
Debug('Have ' .scalar @event_ids . " events for monitor $monitor_id"); Debug('Have ' .scalar @event_ids . ' events for monitor '.$monitor_id);
foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) { foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) {
@ -499,8 +500,8 @@ MAIN: while( $loop ) {
} }
my $age = $Event->age(); my $age = $Event->age();
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { if ( $age and ($age > $Config{ZM_AUDIT_MIN_AGE}) ) {
aud_print( "Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old" ); aud_print("Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old");
if ( confirm() ) { if ( confirm() ) {
$Event->delete_files(); $Event->delete_files();
$cleaned = 1; $cleaned = 1;
@ -586,7 +587,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
} else { } else {
Debug("$$Event{Id} Not found at $path"); Debug("$$Event{Id} Not found at $path");
} }
} } # end foreach Storage
if ( $Event->Archived() ) { if ( $Event->Archived() ) {
Warning("Event $$Event{Id} is Archived. Taking no further action on it."); Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
next; next;
@ -638,18 +639,13 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}"); Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}");
$Event->StorageId($$fs_events{$db_event}->StorageId()); $Event->StorageId($$fs_events{$db_event}->StorageId());
} }
if ( $$fs_events{$db_event}->StartTime() ne $Event->StartTime() ) { if ( ! $Event->StartTime() ) {
Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}"); Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}");
if ( $$Event{Scheme} eq 'Deep' ) {
$Event->StartTime($$fs_events{$db_event}->StartTime()); $Event->StartTime($$fs_events{$db_event}->StartTime());
} else {
$Event->StartTime($$fs_events{$db_event}->StartTime());
}
$Event->save();
} }
$Event->save(); $Event->save();
} } # end if Event exists in db and not in filesystem
} # end if ! in fs_events } # end if ! in fs_events
} # foreach db_event } # foreach db_event
} # end foreach db_monitor } # end foreach db_monitor

View File

@ -351,8 +351,11 @@ sub exportsql {
} }
} }
if ($ARGV[0]) { my $name = $ARGV[0];
$command .= qq( --where="Name = '$ARGV[0]'"); if ($name) {
$name =~ /([A-Za-z0-9 -]*)/; # Only allow alphanumeric, dash and space
$name = $1;
$command .= qq( --where="Name = '$name'");
} }
$command .= " zm Controls MonitorPresets"; $command .= " zm Controls MonitorPresets";

View File

@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, " "SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, " "(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
"`DefaultVideo`, `Scheme`, `SaveJPEGs` FROM `Events` WHERE `Id` = %" PRIu64, event_id); "`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
@ -160,6 +160,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->scheme = Storage::SHALLOW; event_data->scheme = Storage::SHALLOW;
} }
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]); event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8]));
mysql_free_result(result); mysql_free_result(result);
Storage * storage = new Storage(event_data->storage_id); Storage * storage = new Storage(event_data->storage_id);
@ -703,6 +704,30 @@ Debug(1, "Loading image");
Error("Failed getting a frame."); Error("Failed getting a frame.");
return false; return false;
} }
// when stored as an mp4, we just have the rotation as a flag in the headers
// so we need to rotate it before outputting
if ( event_data->Orientation != Monitor::ROTATE_0 ) {
Debug(2, "Rotating image %d", event_data->Orientation);
switch ( event_data->Orientation ) {
case Monitor::ROTATE_0 :
// No action required
break;
case Monitor::ROTATE_90 :
case Monitor::ROTATE_180 :
case Monitor::ROTATE_270 :
image->Rotate((event_data->Orientation-1)*90);
break;
case Monitor::FLIP_HORI :
case Monitor::FLIP_VERT :
image->Flip(event_data->Orientation==Monitor::FLIP_HORI);
break;
default:
Error("Invalid Orientation: %d", event_data->Orientation);
}
} else {
Debug(2, "Not Rotating image %d", event_data->Orientation);
} // end if have rotation
} else { } else {
Error("Unable to get a frame"); Error("Unable to get a frame");
return false; return false;

View File

@ -66,6 +66,7 @@ class EventStream : public StreamBase {
char video_file[PATH_MAX]; char video_file[PATH_MAX];
Storage::Schemes scheme; Storage::Schemes scheme;
int SaveJPEGs; int SaveJPEGs;
Monitor::Orientation Orientation;
}; };
protected: protected:

View File

@ -128,14 +128,14 @@ else
fi; fi;
fi fi
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
if [ "$PPA" == "" ]; then if [ "$PPA" == "" ]; then
if [ "$RELEASE" != "" ]; then if [ "$RELEASE" != "" ]; then
# We need to use our official tarball for the original source, so grab it and overwrite our generated one. # We need to use our official tarball for the original source, so grab it and overwrite our generated one.
IFS='.' read -r -a VERSION <<< "$RELEASE" if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then
PPA="ppa:iconnor/zoneminder-stable" PPA="ppa:iconnor/zoneminder-stable"
else else
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}" PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
fi; fi;
else else
if [ "$BRANCH" == "" ]; then if [ "$BRANCH" == "" ]; then
@ -316,7 +316,7 @@ EOF
read -p "Do you want to upload this binary to zmrepo? (y/N)" read -p "Do you want to upload this binary to zmrepo? (y/N)"
if [[ $REPLY == [yY] ]]; then if [[ $REPLY == [yY] ]]; then
if [ "$RELEASE" != "" ]; then if [ "$RELEASE" != "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/" scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming/"
else else
if [ "$BRANCH" == "" ]; then if [ "$BRANCH" == "" ]; then
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/" scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"

View File

@ -18,7 +18,16 @@ for CMD in sshfs rsync find fusermount mkdir; do
done done
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
if [ "${RELEASE}" != "" ]; then
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
targetfolder="debian/release/mini-dinstall/incoming"
else
targetfolder="debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming"
fi
else
targetfolder="debian/master/mini-dinstall/incoming" targetfolder="debian/master/mini-dinstall/incoming"
fi
else else
targetfolder="travis" targetfolder="travis"
fi fi

View File

@ -1 +1 @@
1.34.0 1.34.2

View File

@ -46,18 +46,18 @@ if ( 0 ) {
SOL_SOCKET, // socket level SOL_SOCKET, // socket level
SO_SNDTIMEO, // timeout option SO_SNDTIMEO, // timeout option
array( array(
"sec"=>0, // Timeout in seconds 'sec'=>0, // Timeout in seconds
"usec"=>500 // I assume timeout in microseconds 'usec'=>500 // I assume timeout in microseconds
) )
); );
$new_stream = null; $new_stream = null;
Info("Testing connection to " . $url_bits['host'].':'.$port); Info('Testing connection to '.$url_bits['host'].':'.$port);
if ( socket_connect( $socket, $url_bits['host'], $port ) ) { if ( socket_connect( $socket, $url_bits['host'], $port ) ) {
$new_stream = $url_bits; // make a copy $new_stream = $url_bits; // make a copy
$new_stream['port'] = $port; $new_stream['port'] = $port;
} else { } else {
socket_close($socket); socket_close($socket);
ZM\Info("No connection to ".$url_bits['host'] . " on port $port"); ZM\Info('No connection to '.$url_bits['host'].' on port '.$port);
continue; continue;
} }
if ( $new_stream ) { if ( $new_stream ) {
@ -92,10 +92,10 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
} }
foreach ( $available_streams as &$stream ) { foreach ( $available_streams as &$stream ) {
# check for existence in db. # check for existence in db.
$stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') ); $stream['url'] = unparse_url($stream, array('path'=>'/','query'=>'action=stream'));
$monitors = ZM\Monitor::find( array('Path'=>$stream['url']) ); $monitors = ZM\Monitor::find(array('Path'=>$stream['url']));
if ( count($monitors) ) { if ( count($monitors) ) {
ZM\Info("Found monitors matching " . $stream['url'] ); ZM\Info('Found monitors matching ' . $stream['url'] );
$stream['Monitor'] = $monitors[0]; $stream['Monitor'] = $monitors[0];
if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) { if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) {
$stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n"; $stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n";
@ -106,11 +106,11 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
} else { } else {
$stream['Monitor'] = clone $defaultMonitor; $stream['Monitor'] = clone $defaultMonitor;
if ( isset($stream['Width']) ) { if ( isset($stream['Width']) ) {
$stream['Monitor']->Width( $stream['Width'] ); $stream['Monitor']->Width($stream['Width']);
$stream['Monitor']->Height( $stream['Height'] ); $stream['Monitor']->Height($stream['Height']);
} }
if ( isset($stream['Name']) ) { if ( isset($stream['Name']) ) {
$stream['Monitor']->Name( $stream['Name'] ); $stream['Monitor']->Name($stream['Name']);
} }
} // Monitor found or not } // Monitor found or not
} // end foreach Stream } // end foreach Stream
@ -121,16 +121,16 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
return $available_streams; return $available_streams;
} // end function probe } // end function probe
if ( canEdit( 'Monitors' ) ) { if ( canEdit('Monitors') ) {
switch ( $_REQUEST['action'] ) { switch ( $_REQUEST['action'] ) {
case 'probe' : case 'probe' :
{ {
$available_streams = array(); $available_streams = array();
$url_bits = null; $url_bits = null;
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url'] ) ) { if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url']) ) {
$url_bits = array( 'host'=>$_REQUEST['url'] ); $url_bits = array('host'=>$_REQUEST['url']);
} else { } else {
$url_bits = parse_url( $_REQUEST['url'] ); $url_bits = parse_url($_REQUEST['url']);
} }
if ( 0 ) { if ( 0 ) {
@ -147,13 +147,13 @@ if ( 0 ) {
} }
if ( ! $url_bits ) { if ( ! $url_bits ) {
ajaxError("The given URL was too malformed to parse."); ajaxError('The given URL was too malformed to parse.');
return; return;
} }
$available_streams = probe( $url_bits ); $available_streams = probe($url_bits);
ajaxResponse( array('Streams'=>$available_streams) ); ajaxResponse(array('Streams'=>$available_streams));
return; return;
} // end case url_probe } // end case url_probe
case 'import': case 'import':
@ -161,16 +161,16 @@ if ( 0 ) {
$file = $_FILES['import_file']; $file = $_FILES['import_file'];
if ($file["error"] > 0) { if ( $file['error'] > 0 ) {
ajaxError($file["error"]); ajaxError($file['error']);
return; return;
} else { } else {
$filename = $file["name"]; $filename = $file['name'];
$available_streams = array(); $available_streams = array();
$row = 1; $row = 1;
if (($handle = fopen($file['tmp_name'], 'r')) !== FALSE) { if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { while ( ($data = fgetcsv($handle, 1000, ',')) !== FALSE ) {
$name = $data[0]; $name = $data[0];
$url = $data[1]; $url = $data[1];
$group = $data[2]; $group = $data[2];
@ -178,16 +178,16 @@ if ( 0 ) {
$url_bits = null; $url_bits = null;
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $url) ) { if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $url) ) {
$url_bits = array( 'host'=>$url, 'scheme'=>'http' ); $url_bits = array('host'=>$url, 'scheme'=>'http');
} else { } else {
$url_bits = parse_url( $url ); $url_bits = parse_url($url);
} }
if ( ! $url_bits ) { if ( ! $url_bits ) {
ZM\Info("Bad url, skipping line $name $url $group"); ZM\Info("Bad url, skipping line $name $url $group");
continue; continue;
} }
$available_streams += probe( $url_bits ); $available_streams += probe($url_bits);
//$url_bits['url'] = unparse_url( $url_bits ); //$url_bits['url'] = unparse_url( $url_bits );
//$url_bits['Monitor'] = $defaultMonitor; //$url_bits['Monitor'] = $defaultMonitor;
@ -197,23 +197,19 @@ if ( 0 ) {
} // end while rows } // end while rows
fclose($handle); fclose($handle);
ajaxResponse( array('Streams'=>$available_streams) ); ajaxResponse(array('Streams'=>$available_streams));
} else { } else {
ajaxError("Uploaded file does not exist"); ajaxError('Uploaded file does not exist');
return; return;
} }
} }
} // end case import } // end case import
default: default:
{ ZM\Warning('unknown action '.$_REQUEST['action']);
ZM\Warning("unknown action " . $_REQUEST['action'] ); } // end switch action
} // end ddcase default
}
} else { } else {
ZM\Warning("Cannot edit monitors" ); ZM\Warning('Cannot edit monitors');
} }
ajaxError( 'Unrecognised action or insufficient permissions' ); ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
?> ?>

View File

@ -10,9 +10,9 @@ if ( canEdit('Monitors') ) {
$dbConn->exec('LOCK TABLES Monitors WRITE'); $dbConn->exec('LOCK TABLES Monitors WRITE');
for ( $i = 0; $i < count($monitor_ids); $i += 1 ) { for ( $i = 0; $i < count($monitor_ids); $i += 1 ) {
$monitor_id = $monitor_ids[$i]; $monitor_id = $monitor_ids[$i];
$monitor_id = preg_replace( '/^monitor_id-/', '', $monitor_id ); $monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id);
if ( ( ! $monitor_id ) or ! ( is_integer( $monitor_id ) or ctype_digit( $monitor_id ) ) ) { if ( ( !$monitor_id ) or ! ( is_integer($monitor_id) or ctype_digit($monitor_id) ) ) {
Warning("Got $monitor_id from " . $monitor_ids[$i]); Warning('Got '.$monitor_id.' from '.$monitor_ids[$i]);
continue; continue;
} }
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id)); dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
@ -23,13 +23,11 @@ if ( canEdit('Monitors') ) {
return; return;
} // end case sort } // end case sort
default: default:
{ ZM\Warning('unknown action '.$_REQUEST['action']);
ZM\Warning('unknown action ' . $_REQUEST['action']);
} // end ddcase default
} }
} else { } else {
ZM\Warning('Cannot edit monitors'); ZM\Warning('Cannot edit monitors');
} }
ajaxError('Unrecognised action or insufficient permissions'); ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
?> ?>

View File

@ -155,5 +155,5 @@ if ( canEdit('Events') ) {
} // end switch action } // end switch action
} // end if canEdit('Events') } // end if canEdit('Events')
ajaxError('Unrecognised action or insufficient permissions'); ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
?> ?>

View File

@ -49,14 +49,23 @@ if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == '
// as it produces the same error as when you don't answer a recaptcha // as it produces the same error as when you don't answer a recaptcha
if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) { if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) { if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) {
Error('reCaptcha authentication failed'); ZM\Error('reCaptcha authentication failed. response was: ' . print_r($responseData['error-codes'],true));
unset($user); // unset should be ok here because we aren't in a function unset($user); // unset should be ok here because we aren't in a function
return; return;
} else { } else {
Error('Invalid recaptcha secret detected'); ZM\Error('Invalid recaptcha secret detected');
} }
} }
} // end if success==false } // end if success==false
if ( ! (empty($_REQUEST['username']) or empty($_REQUEST['password'])) ) {
$ret = validateUser($_REQUEST['username'], $_REQUEST['password']);
if ( !$ret[0] ) {
ZM\Error($ret[1]);
unset($user); // unset should be ok here because we aren't in a function
} else {
$user = $ret[0];
}
} # end if have username and password
} // end if using reCaptcha } // end if using reCaptcha
// if captcha existed, it was passed // if captcha existed, it was passed

View File

@ -193,7 +193,8 @@ if ( ! defined('ZM_SERVER_ID') ) {
} }
} }
ini_set('date.timezone', ZM_TIMEZONE); if ( ZM_TIMEZONE )
ini_set('date.timezone', ZM_TIMEZONE);
function process_configfile($configFile) { function process_configfile($configFile) {
if ( is_readable( $configFile ) ) { if ( is_readable( $configFile ) ) {

View File

@ -202,7 +202,8 @@ isset($action) || $action = NULL;
if ( (!$view and !$request) or ($view == 'console') ) { if ( (!$view and !$request) or ($view == 'console') ) {
// Verify the system, php, and mysql timezones all match // Verify the system, php, and mysql timezones all match
date_default_timezone_set(ZM_TIMEZONE); #if ( ZM_TIMEZONE )
#date_default_timezone_set(ZM_TIMEZONE);
check_timezone(); check_timezone();
} }
@ -245,7 +246,6 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n
if ( ! $request ) { if ( ! $request ) {
zm_session_start(); zm_session_start();
$_SESSION['postLoginQuery'] = $_SERVER['QUERY_STRING']; $_SESSION['postLoginQuery'] = $_SERVER['QUERY_STRING'];
ZM\Error("postLoginQuery " . $_SESSION['postLoginQuery']);
session_write_close(); session_write_close();
} }
$request = null; $request = null;

View File

@ -774,6 +774,7 @@ $SLANG = array(
'TurboPanSpeed' => 'Turbo Pan Speed', 'TurboPanSpeed' => 'Turbo Pan Speed',
'TurboTiltSpeed' => 'Turbo Tilt Speed', 'TurboTiltSpeed' => 'Turbo Tilt Speed',
'Type' => 'Type', 'Type' => 'Type',
'TZUnset' => 'Unset - use value in php.ini',
'Unarchive' => 'Unarchive', 'Unarchive' => 'Unarchive',
'Undefined' => 'Undefined', 'Undefined' => 'Undefined',
'Units' => 'Units', 'Units' => 'Units',

View File

@ -21,3 +21,8 @@ input.large {
#contentTable.userTable .colMonitor, #contentTable.userTable .colUsername { #contentTable.userTable .colMonitor, #contentTable.userTable .colUsername {
text-align: left; text-align: left;
} }
input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SITEKEY]"],
input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"] {
width: 100%;
}

View File

@ -125,7 +125,7 @@ function controlPanTilt($monitor, $cmds) {
<button type="button" class="arrowBtn upLeftBtn<?php echo $hasDiag?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUpLeft'] ?>',event,-1,-1)"></button> <button type="button" class="arrowBtn upLeftBtn<?php echo $hasDiag?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUpLeft'] ?>',event,-1,-1)"></button>
<button type="button" class="arrowBtn upBtn<?php echo $hasTilt?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUp'] ?>',event,0,-1)"></button> <button type="button" class="arrowBtn upBtn<?php echo $hasTilt?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUp'] ?>',event,0,-1)"></button>
<button type="button" class="arrowBtn upRightBtn<?php echo $hasDiag?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUpRight'] ?>',event,1,-1)"></button> <button type="button" class="arrowBtn upRightBtn<?php echo $hasDiag?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUpRight'] ?>',event,1,-1)"></button>
<button type="button" class="arrowBtn leftBtn<?php echo $hasPan?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveLeft'] ?>',event,1,0)"></button> <button type="button" class="arrowBtn leftBtn<?php echo $hasPan?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveLeft'] ?>',event,-1,0)"></button>
<?php if ( isset($cmds['Center']) ) { ?> <?php if ( isset($cmds['Center']) ) { ?>
<button type="button" class="arrowBtn centerBtn" onclick="controlCmd('<?php echo $cmds['Center'] ?>')"></button> <button type="button" class="arrowBtn centerBtn" onclick="controlCmd('<?php echo $cmds['Center'] ?>')"></button>
<?php } else { ?> <?php } else { ?>

View File

@ -423,7 +423,14 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) {
$storage_areas = $storage_areas_with_no_server_id; $storage_areas = $storage_areas_with_no_server_id;
if ( count($storage_areas) <= 4 ) if ( count($storage_areas) <= 4 )
echo implode(', ', array_map($func, $storage_areas)); echo implode(', ', array_map($func, $storage_areas));
echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%'; $shm_percent = getDiskPercent(ZM_PATH_MAP);
$class = '';
if ( $shm_percent > 98 ) {
$class = 'error';
} else if ( $shm_percent > 90 ) {
$class = 'warning';
}
echo ' <span class="'.$class.'">'.ZM_PATH_MAP.': '.$shm_percent.'%</span>';
?></li> ?></li>
</ul> </ul>
<?php if ( defined('ZM_WEB_CONSOLE_BANNER') and ZM_WEB_CONSOLE_BANNER != '' ) { ?> <?php if ( defined('ZM_WEB_CONSOLE_BANNER') and ZM_WEB_CONSOLE_BANNER != '' ) { ?>

View File

@ -393,15 +393,16 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $
$configCats[$tab]['ZM_SKIN_DEFAULT']['Hint'] = join('|', array_map('basename', glob('skins/*',GLOB_ONLYDIR))); $configCats[$tab]['ZM_SKIN_DEFAULT']['Hint'] = join('|', array_map('basename', glob('skins/*',GLOB_ONLYDIR)));
$configCats[$tab]['ZM_CSS_DEFAULT']['Hint'] = join('|', array_map ( 'basename', glob('skins/'.ZM_SKIN_DEFAULT.'/css/*',GLOB_ONLYDIR) )); $configCats[$tab]['ZM_CSS_DEFAULT']['Hint'] = join('|', array_map ( 'basename', glob('skins/'.ZM_SKIN_DEFAULT.'/css/*',GLOB_ONLYDIR) ));
$configCats[$tab]['ZM_BANDWIDTH_DEFAULT']['Hint'] = $bandwidth_options; $configCats[$tab]['ZM_BANDWIDTH_DEFAULT']['Hint'] = $bandwidth_options;
function timezone_list() { function timezone_list() {
static $timezones = null; static $timezones = null;
if ($timezones === null) { if ( $timezones === null ) {
$timezones = []; $timezones = [];
$offsets = []; $offsets = [];
$now = new DateTime('now', new DateTimeZone('UTC')); $now = new DateTime('now', new DateTimeZone('UTC'));
foreach (DateTimeZone::listIdentifiers() as $timezone) { foreach ( DateTimeZone::listIdentifiers() as $timezone ) {
$now->setTimezone(new DateTimeZone($timezone)); $now->setTimezone(new DateTimeZone($timezone));
$offsets[] = $offset = $now->getOffset(); $offsets[] = $offset = $now->getOffset();
$timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone); $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone);
@ -411,23 +412,22 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $
} }
return $timezones; return $timezones;
} }
function format_GMT_offset($offset) { function format_GMT_offset($offset) {
$hours = intval($offset / 3600); $hours = intval($offset / 3600);
$minutes = abs(intval($offset % 3600 / 60)); $minutes = abs(intval($offset % 3600 / 60));
return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : ''); return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : '');
} }
function format_timezone_name($name) { function format_timezone_name($name) {
$name = str_replace('/', ', ', $name); $name = str_replace('/', ', ', $name);
$name = str_replace('_', ' ', $name); $name = str_replace('_', ' ', $name);
$name = str_replace('St ', 'St. ', $name); $name = str_replace('St ', 'St. ', $name);
return $name; return $name;
}
$configCats[$tab]['ZM_TIMEZONE']['Hint'] = timezone_list();
} }
$configCats[$tab]['ZM_TIMEZONE']['Hint'] = array(''=> translate('TZUnset')) + timezone_list();
} # end if tab == system
?> ?>
<form name="optionsForm" class="form-horizontal" method="post" action="?"> <form name="optionsForm" class="form-horizontal" method="post" action="?">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>

View File

@ -200,7 +200,7 @@ if ( empty($_REQUEST['path']) ) {
header('HTTP/1.0 404 Not Found'); header('HTTP/1.0 404 Not Found');
ZM\Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() ); ZM\Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() );
} }
$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path; $command = ZM_PATH_FFMPEG.' -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path;
#$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; #$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
#$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; #$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
ZM\Logger::Debug("Running $command"); ZM\Logger::Debug("Running $command");