Merge branch 'master' into control_plugin_psia

pull/2191/head
Andrew Bauer 2018-10-11 09:32:06 -05:00 committed by GitHub
commit 7ffc5ab266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
144 changed files with 4821 additions and 2643 deletions

View File

@ -42,7 +42,7 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own.
### Building a ZoneMinder Package
### Building a ZoneMinder Package ###
Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier.

View File

@ -782,6 +782,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
--
-- Add some monitor preset values
@ -824,6 +825,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);

View File

@ -5,7 +5,7 @@
-- Add Refresh column to Monitors table
--
ALTER TABLE `zm`.`Monitors`
ALTER TABLE `Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(

2
db/zm_update-1.31.47.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL;

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

@ -0,0 +1,5 @@
--
-- This updates a 1.31.47 database to 1.32.0
--
-- No changes required
--

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

@ -0,0 +1,5 @@
--
-- This updates a 1.32.0 database to 1.32.1
--
-- No changes required
--

View File

@ -37,11 +37,13 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl
, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl
, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libjson-maybexs-perl
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl
,libnumber-bytes-human-perl
,libfile-slurp-perl
, libpcre3
, ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57
, rsyslog | system-log-daemon

View File

@ -1,4 +1,4 @@
# CMakeLists.txt for the Redhat/CentOS Target Distro.
# CMakeLists.txt for the Redhat Target Distros.
# Display a message to show the RHEL build options are being processed.
if(ZM_TARGET_DISTRO MATCHES "^el")
@ -20,11 +20,16 @@ if(ZM_WEB_USER STREQUAL "nginx")
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README COPYONLY)
else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
if( ZM_TARGET_DISTRO MATCHES "^fc")
configure_file(readme/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README COPYONLY)
else( ZM_TARGET_DISTRO MATCHES "^fc")
configure_file(readme/README.Redhat7 ${CMAKE_CURRENT_SOURCE_DIR}/readme/README COPYONLY)
endif( ZM_TARGET_DISTRO MATCHES "^fc")
endif(ZM_WEB_USER STREQUAL "nginx")
# Create several empty folders

View File

@ -72,7 +72,7 @@ New installs
6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
using the default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
Inspect the web server configuration file and verify it meets your needs:
@ -129,7 +129,7 @@ New installs
Upgrades
========
1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
1. Conf.d folder support has been added to ZoneMinder. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
@ -151,6 +151,10 @@ Upgrades
exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary.
The contents of this file must be merged into your Apache configuration.
See step 6 of the installation section if you have not already done this
during a previous upgrade.
4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command:

View File

@ -72,7 +72,7 @@ New installs
6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
using the default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
Inspect the web server configuration file and verify it meets your needs:
@ -129,7 +129,7 @@ New installs
Upgrades
========
1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
1. Conf.d folder support has been added to ZoneMinder. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
@ -147,10 +147,14 @@ Upgrades
3. Verify the ZoneMinder Apache configuration file in the folder
/etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
may also be a file called "zoneminder.conf.rpmnew". If an rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary.
The contents of this file must be merged into your Apache configuration.
See step 6 of the installation section if you have not already done this
during a previous upgrade.
4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command:

View File

@ -22,11 +22,10 @@
%global with_apcu_bc 1
%endif
%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
%global _hardened_build 1
Name: zoneminder
Version: 1.31.45
Version: 1.32.1
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@ -203,8 +202,8 @@ fi
%{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || :
# Warn the end user to read the README file
echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.%{readme_suffix} to finish the\ninstallation or upgrade!\n"
echo -e "\nThe README file is located here: %{_docdir}/%{name}\n"
echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, you must read the README file\nto finish the installation or upgrade!"
echo -e "\nThe README file is located here: %{_pkgdocdir}/README\n"
%if 0%{?with_nginx}
# Nginx does not create an SSL certificate like the apache package does so lets do that here
@ -252,7 +251,7 @@ EOF
%files
%license COPYING
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https
%doc AUTHORS README.md distros/redhat/readme/README distros/redhat/readme/README.https
# We want these two folders to have "normal" read permission
# compared to the folder contents
@ -321,11 +320,36 @@ EOF
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog
* Sun Apr 22 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.31.42-1
- Remove support for sysvinit a.k.a. el6
- use desktop-file-install for new zoneminder.desktop file
- add new web cache folder
- 1.31.42 development snapshot
* Tue Oct 2 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.1-1
- 1.32.1 release
- Bug fix release
* Wed Sep 12 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.0-1
- 1.32.0 release
- remove el6 (sys v init) support
- Make README name consistent across all supported distros
- remove jscalendar
- add requires php-json, zip
- support zm/conf.d folder
- support zm cache (busting) folder
* Sun Aug 19 2018 Leigh Scott <leigh123linux@googlemail.com> - 1.30.4-9
- Rebuilt for Fedora 29 Mass Rebuild binutils issue
* Fri Jul 27 2018 RPM Fusion Release Engineering <leigh123linux@gmail.com> - 1.30.4-8
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Thu Mar 08 2018 RPM Fusion Release Engineering <leigh123linux@googlemail.com> - 1.30.4-7
- Rebuilt for new ffmpeg snapshot
* Thu Mar 01 2018 RPM Fusion Release Engineering <leigh123linux@googlemail.com> - 1.30.4-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Thu Jan 18 2018 Leigh Scott <leigh123linux@googlemail.com> - 1.30.4-5
- Rebuilt for ffmpeg-3.5 git
* Thu Aug 31 2017 RPM Fusion Release Engineering <kwizart@rpmfusion.org> - 1.30.4-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1
- modify autosetup macro parameters

View File

@ -50,6 +50,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdevice-serialport-perl
,libimage-info-perl
,libjson-any-perl
,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl

View File

@ -28,7 +28,7 @@ override_dh_auto_configure:
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_clean:
dh_clean $(MANPAGES1)

View File

@ -32,7 +32,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl,
libsys-mmap-perl, liburi-encode-perl, libwww-perl
Description: Perl libraries for ZoneMinder
ZoneMinder is a video camera security and surveillance solution.

View File

@ -45,7 +45,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl,
libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl,
libsys-mmap-perl, liburi-encode-perl, libwww-perl
Description: Perl libraries for ZoneMinder
ZoneMinder is a video camera security and surveillance solution.

View File

@ -53,6 +53,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdevice-serialport-perl
,libimage-info-perl
,libjson-any-perl
,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl

View File

@ -22,7 +22,7 @@ if [ "$1" = "configure" ]; then
if [ "$ZM_DB_HOST" = "localhost" ]; then
if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; then
if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then
# Ensure zoneminder is stopped
deb-systemd-invoke stop zoneminder.service || exit $?
@ -68,6 +68,7 @@ if [ "$1" = "configure" ]; then
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
echo "Done Updating; starting ZoneMinder."
else
echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi
@ -78,7 +79,6 @@ if [ "$1" = "configure" ]; then
else
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
fi
echo "Done Updating; starting ZoneMinder."
deb-systemd-invoke restart zoneminder.service
fi

View File

@ -13,6 +13,13 @@ The API is built in CakePHP and lives under the ``/api`` directory. It
provides a RESTful service and supports CRUD (create, retrieve, update, delete)
functions for Monitors, Events, Frames, Zones and Config.
Enabling API
^^^^^^^^^^^^
A default ZoneMinder installs with APIs enabled. You can explictly enable/disable the APIs
via the Options->System menu by enabling/disabling ``OPT_USE_API``. Note that if you intend
to use APIs with 3rd party apps, such as zmNinja or others that use APIs, you should also
enable ``AUTH_HASH_LOGINS``.
Login, Logout & API Security
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The APIs tie into ZoneMinder's existing security model. This means if you have
@ -29,13 +36,13 @@ This means if you plan to use cuRL to experiment with these APIs, you first need
::
curl -XPOST -d "user=XXXX&pass=YYYY" -c cookies.txt http://yourzmip/zm/api/login.json
curl -XPOST -d "user=XXXX&pass=YYYY" -c cookies.txt http://yourzmip/zm/api/host/login.json
Staring ZM 1.32.0, you also have a `logout` API that basically clears your session. It looks like this:
::
curl -b cookies.txt http://yourzmip/zm/api/logout.json
curl -b cookies.txt http://yourzmip/zm/api/host/logout.json
**Login process for older versions of ZoneMinder**

View File

@ -35,7 +35,7 @@ guide you with a quick search.
`releases page <https://github.com/ZoneMinder/zoneminder/releases>`_ for
the latest release.
Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately
Alternatively, the ZoneMinder project team maintains a `PPA <https://askubuntu.com/questions/4983/what-are-ppas-and-how-do-i-use-them>`_, which is updated immediately
following a new release of ZoneMinder. To use this repository instead of the
official Ubuntu repository, enter the following from the command line:
@ -43,6 +43,15 @@ guide you with a quick search.
add-apt-repository ppa:iconnor/zoneminder
Please note that as of 1.32.0 We are creating a new PPA for each major version, as a means to prevent automatic upgrades from one major version to another. So instead of the above ppa line use the following:
::
add-apt-repository ppa:iconnor/zoneminder-1.32
If you are on trusty, you may want to add both, as there are some packages for dependencies included in the old ppa.
Update repo and upgrade.
::
@ -51,6 +60,7 @@ Update repo and upgrade.
apt-get upgrade
apt-get dist-upgrade
**Step 3:** Configure MySQL
.. sidebar :: Note
@ -62,8 +72,10 @@ Update repo and upgrade.
| /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
| /etc/mysql/mysql.cnf is a basic file
Certain new defaults in MySQL 5.7 are currently causing some issues with ZoneMinder,
the workaround is to modify the sql_mode setting of MySQL.
Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0,
the workaround is to modify the sql_mode setting of MySQL. Please note that these
changes are NOT required for ZoneMinder 1.32.0 and some people have reported them
causing problems in 1.32.0.
To better manage the MySQL server it is recommended to copy the sample config file and
replace the default my.cnf symbolic link.
@ -104,10 +116,12 @@ Restart MySQL
**Step 5:** Configure the ZoneMinder Database
This step should not be required on ZoneMinder 1.32.0.
::
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';"
mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';"
**Step 6:** Set permissions
@ -124,10 +138,17 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content
::
a2enconf zoneminder
a2enmod cgi
a2enconf zoneminder
a2enmod cgi
a2enmod rewrite
You may also want to enable to following modules to improve caching performance
::
a2enmod expires
a2enmod headers
**Step 8:** Enable and start Zoneminder
::

View File

@ -150,7 +150,7 @@ Orientation
WebSite
^^^^^^^
This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
This Source Type allows one to configure an arbitrary website as a non-recordable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
Website URL
Enter the full http or https url to the desired website.

View File

@ -15,7 +15,7 @@ AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is
AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help.
AUTH_HASH_LOGINS - The normal process for logging into ZoneMinder is via the login screen with username and password. In some circumstances it may be desirable to allow access directly to one or more pages, for instance from a third party application. If this option is enabled then adding an 'auth' parameter to any request will include a shortcut login bypassing the login screen, if not already logged in. As authentication hashes are time and, optionally, IP limited this can allow short-term access to ZoneMinder screens from other web pages etc. In order to use this the calling application will hae to generate the authentication hash itself and ensure it is valid. If you use this option you should ensure that you have modified the ZM_AUTH_HASH_SECRET to somethign unique to your system.
AUTH_HASH_LOGINS - The normal process for logging into ZoneMinder is via the login screen with username and password. In some circumstances it may be desirable to allow access directly to one or more pages, for instance from a third party application. If this option is enabled then adding an 'auth' parameter to any request will include a shortcut login bypassing the login screen, if not already logged in. As authentication hashes are time and, optionally, IP limited, this can allow short-term access to ZoneMinder screens from other web pages etc. In order to use this, the calling application will have to generate the authentication hash itself and ensure it is valid. If you use this option you should ensure that you have modified the ZM_AUTH_HASH_SECRET to something unique to your system.
OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. It is recommended that you set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later.
@ -38,6 +38,7 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu
OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.
CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable
UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of http://<proxy host>:<proxy port>/
SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored.

21
misc/apache-cors.conf Normal file
View File

@ -0,0 +1,21 @@
# This configuration is only needed for compatibility with zmninja
# If not using VirtualHosts, copy or symlink this file into the Apache config folder
# If using VirtualHosts, then this config must be placed inside the appropriate
# <VirtualHost> directive.
# Make sure you have enabled/loaded header manipulation modules
# For example, in Debian based distros the command is "sudo a2enmod headers"
# zmNinja header permissions. Tweak to your needs
Header always set Access-Control-Allow-Credentials true
#zmNinja's WKWebView will set the origin header as localhost:8080
Header always set Access-Control-Allow-Origin "http://localhost:8080"
Header always set Access-Control-Request-Methods "Authorization"
Header always set Access-Control-Methods "OPTIONS,GET,POST,DELETE,PUT"
Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization, Origin, Accept, client-security-token"
Header always set Access-Control-Expose-Headers "Content-Security-Policy, Location"
Header always set Access-Control-Max-Age "1000"
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

View File

@ -1,27 +1,3 @@
# ==========================================================================
#
# ZoneMinder Base Module, $Date$, $Revision$
# Copyright (C) 2001-2008 Philip Coombes
#
# 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::Base;
use 5.006;
@ -82,11 +58,18 @@ Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2008 Philip Coombes
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 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.
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.
=cut

View File

@ -409,8 +409,8 @@ our @options = (
that is used to get notifications for alarms detected by ZoneMinder
in real time. zmNinja requires this server for push notifications to
mobile phones. This option only enables the server if its already installed.
Please visit https://github.com/pliablepixels/zmeventserver for installation
instructions.
Please visit the [zmeventserver project site](https://github.com/pliablepixels/zmeventserver)
for installation instructions.
`,
type => $types{boolean},
category => 'system',
@ -442,7 +442,7 @@ our @options = (
description => 'Your recaptcha site-key',
help => q`You need to generate your keys from
the Google reCaptcha website.
Please refer to https://www.google.com/recaptcha/
Please refer to the [recaptcha project site](https://www.google.com/recaptcha/)
for more details.
`,
requires => [
@ -457,7 +457,7 @@ our @options = (
description => 'Your recaptcha secret-key',
help => q`You need to generate your keys from
the Google reCaptcha website.
Please refer to https://www.google.com/recaptcha/
Please refer to the [recaptcha project site](https://www.google.com/recaptcha/)
for more details.
`,
requires => [
@ -674,9 +674,9 @@ our @options = (
ZoneMinder uses to view image streams on browsers such as
Internet Explorer that don't natively support this format. If
you use this browser it is highly recommended to install this
from http://www.charliemouse.com/code/cambozola/ however if it
is not installed still images at a lower refresh rate can still
be viewed.
from the [cambozola project site](http://www.charliemouse.com/code/cambozola/).
However, if it is not installed still images at a lower refresh rate can
still be viewed.
`,
type => $types{boolean},
category => 'images',
@ -690,9 +690,9 @@ our @options = (
ZoneMinder uses to view image streams on browsers such as
Internet Explorer that don't natively support this format. If
you use this browser it is highly recommended to install this
from http://www.charliemouse.com/code/cambozola/ however if it
is not installed still images at a lower refresh rate can still
be viewed. Leave this as 'cambozola.jar' if cambozola is
from the [cambozola project site](http://www.charliemouse.com/code/cambozola/).
However if it is not installed still images at a lower refresh rate can
still be viewed. Leave this as 'cambozola.jar' if cambozola is
installed in the same directory as the ZoneMinder web client
files.
`,
@ -2721,7 +2721,8 @@ our @options = (
This is being done for the sole purpoase of creating a better
product for our target audience. This script is intended to be
completely transparent to the end user, and can be disabled from
the web console under Options.
the web console under Options. For more details on what information
we collect, please refer to our [privacy](?view=privacy) statement.
`,
type => $types{boolean},
category => 'system',
@ -3882,6 +3883,15 @@ our @options = (
readonly => 1,
category => 'dynamic',
},
{
name => 'ZM_SHOW_PRIVACY',
default => 'yes',
description => 'Present the privacy statment',
help => '',
type => $types{boolean},
readonly => 1,
category => 'dynamic',
},
{
name => 'ZM_SSMTP_MAIL',
default => 'no',
@ -3898,7 +3908,7 @@ our @options = (
SSMTP is a lightweight and efficient method to send email.
The SSMTP application is not installed by default.
NEW_MAIL_MODULES must also be enabled.
Please visit: http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder
Please visit the ZoneMinder [SSMTP Wiki page](http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder)
for setup and configuration help.
`,
type => $types{boolean},

View File

@ -0,0 +1,362 @@
package ZoneMinder::Control::Dahua;
use 5.8.0;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our $REALM = '';
our $USERNAME = '';
our $PASSWORD = '';
our $ADDRESS = '';
our $PROTOCOL = 'http://';
use Time::HiRes qw(usleep);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use ZoneMinder::Database qw(zmDbConnect);
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
$self->loadMonitor();
# The Dahua camera firmware API supports the concept of having multiple
# channels on a single IP controller.
# As most cameras only have a single channel, and there is no similar
# information model in Zoneminder, I'm hardcoding the first and default
# channel "0", here.
$self->{dahua_channel_number} = "0";
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';
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent("ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'closed';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug("sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
# Detect REALM
my $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz";
my $req = HTTP::Request->new(GET=>$get_config_url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$self->{state} = 'open';
return;
}
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k (keys %$headers) {
Debug("Initial Header $k => $$headers{$k}");
}
if ($$headers{'www-authenticate'}) {
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ($tokens =~ /\w+="([^"]+)"/i) {
if ($REALM ne $1) {
$REALM = $1;
Debug("Changing REALM to '" . $REALM . "'");
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
my $req = HTTP::Request->new(GET=>$get_config_url);
$res = $self->{ua}->request($req);
if ($res->is_success()) {
$self->{state} = 'open';
return;
}
Debug('Authentication still failed after updating REALM' . $res->status_line);
$headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
} else {
Error('Authentication failed, not a REALM problem');
}
} else {
Error('Failed to match realm in tokens');
} # end if
} else {
Error('No WWW-Authenticate Header');
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendGetRequest {
my $self = shift;
my $url_path = shift;
my $result = undef;
my $url = $PROTOCOL . $ADDRESS . $url_path;
my $req = HTTP::Request->new(GET=>$url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
if ($res->status_line() eq '401 Unauthorized') {
Debug("Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD);
Debug("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error("Error check failed: '".$res->status_line());
}
}
return($result);
}
sub sendPtzCommand
{
my $self = shift;
my $action = shift;
my $command_code = shift;
my $arg1 = shift;
my $arg2 = shift;
my $arg3 = shift;
my $channel = $self->{dahua_channel_number};
my $url_path = "/cgi-bin/ptz.cgi?";
$url_path .= "action=" . $action . "&";
$url_path .= "channel=" . $channel . "&";
$url_path .= "code=" . $command_code . "&";
$url_path .= "arg1=" . $arg1 . "&";
$url_path .= "arg2=" . $arg2 . "&";
$url_path .= "arg3=" . $arg3;
$self->sendGetRequest($url_path);
}
sub sendMomentaryPtzCommand
{
my $self = shift;
my $command_code = shift;
my $arg1 = shift;
my $arg2 = shift;
my $arg3 = shift;
my $duration_ms = shift;
$self->sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
my $duration_ns = $duration_ms * 1000;
usleep($duration_ns);
$self->sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
}
sub moveRelUpLeft
{
my $self = shift;
Debug("Move Up Left");
$self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
}
sub moveRelUp
{
my $self = shift;
Debug("Move Up");
$self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
}
sub moveRelUpRight
{
my $self = shift;
Debug("Move Up Right");
$self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
}
sub moveRelLeft
{
my $self = shift;
Debug("Move Left");
$self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
}
sub moveRelRight
{
my $self = shift;
Debug("Move Right");
$self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
}
sub moveRelDownLeft
{
my $self = shift;
Debug("Move Down Left");
$self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
}
sub moveRelDown
{
my $self = shift;
Debug("Move Down");
$self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
}
sub moveRelDownRight
{
my $self = shift;
Debug("Move Down Right");
$self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
}
sub zoomRelTele
{
my $self = shift;
Debug("Zoom Relative Tele");
$self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
}
sub zoomRelWide
{
my $self = shift;
Debug("Zoom Relative Wide");
$self->sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
}
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
$self->sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
my $dbh = zmDbConnect(1);
my $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?';
my $sth = $dbh->prepare($sql)
or Fatal("Can't prepare sql '$sql': " . $dbh->errstr());
my $res = $sth->execute($self->{Monitor}->{Id}, $preset_id)
or Fatal("Can't execute sql '$sql': " . $sth->errstr());
my $control_preset_row = $sth->fetchrow_hashref();
my $new_label_name = $control_preset_row->{'Label'};
$self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
$self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
$self->sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
}
1;
__END__
=head1 NAME
ZoneMinder::Control::Dahua - Perl module for Dahua cameras
=head1 SYNOPSIS
use ZoneMinder::Control::Dahua;
place this in /usr/share/perl5/ZoneMinder/Control
=head1 DESCRIPTION
This module is an implementation of the Dahua IP camera HTTP control API.
=head2 EXPORT
None by default.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2018 ZoneMinder LLC
This library 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 library 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.
=cut

View File

@ -30,10 +30,10 @@ sub open {
$self->loadMonitor();
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};
$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};

View File

@ -232,11 +232,11 @@ sub start_transaction {
sub end_transaction {
#my ( $caller, undef, $line ) = caller;
#$openprint::log->debug("Called end_transaction from $caller : $line");
my ( $d, $ac ) = @_;
if ( ! defined $ac ) {
Error("Undefined ac");
}
#$openprint::log->debug("Called end_transaction from $caller : $line");
my ( $d, $ac ) = @_;
if ( ! defined $ac ) {
Error("Undefined ac");
}
$d = $dbh if ! $d;
if ( $ac ) {
#$log->debug("Committing");
@ -244,55 +244,31 @@ if ( ! defined $ac ) {
} # end if
$d->{AutoCommit} = $ac;
} # end sub end_transaction
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for blah blah blah
ZoneMinder::Database - Perl module containing database functions used in ZM
=head1 SYNOPSIS
use ZoneMinder::Database;
blah blah blah
=head1 DESCRIPTION
Stub documentation for ZoneMinder, created by h2xs. It looks like the
author of the extension was negligent enough to leave the stub
unedited.
Blah blah blah.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Mention other useful documentation such as the documentation of
related modules or operating system documentation (such as man pages
in UNIX), or any relevant external documentation such as RFCs or
standards.
If you have a mailing list set up for your module, mention it here.
If you have a web site set up for your module, mention it here.
zmDbConnect
zmDbDisconnect
zmDbGetMonitors
zmDbGetMonitor
zmDbGetMonitorAndControl
=head1 AUTHOR
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2008 Philip Coombes
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

View File

@ -35,7 +35,6 @@ require Date::Manip;
require File::Find;
require File::Path;
require File::Copy;
require File::Slurp;
require File::Basename;
require Number::Bytes::Human;
@ -239,7 +238,7 @@ sub LinkPath {
'.'.$$event{Id}
);
} elsif ( $$event{Path} ) {
if ( ( $$event{Path} =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) {
if ( ( $event->RelativePath() =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) {
$$event{LinkPath} = $1.'/.'.$$event{Id};
} else {
Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}");
@ -255,6 +254,33 @@ sub LinkPath {
return $$event{LinkPath};
} # end sub LinkPath
sub createPath {
makePath($_[0]->Path());
}
sub createLinkPath {
my $LinkPath = $_[0]->LinkPath();
my $EventPath = $_[0]->EventPath();
if ( $LinkPath ) {
if ( !symlink($EventPath, $LinkPath) ) {
Error("Failed symlinking $EventPath to $LinkPath");
}
}
}
sub idPath {
return sprintf('%s/.%d', $_[0]->Path(), $_[0]->{Id});
}
sub createIdFile {
my $event = shift;
my $idFile = $event->idPath();
open( my $ID_FP, '>', $idFile )
or Error("Can't open $idFile: $!");
close($ID_FP);
setFileOwner($idFile);
}
sub GenerateVideo {
my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_;
@ -417,13 +443,13 @@ sub delete_files {
return;
}
my $event_path = $event->RelativePath();
Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path.");
Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path, scheme is $$event{Scheme}.");
if ( $event_path ) {
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $deleted = 0;
if ( $$Storage{Type} eq 's3fs' ) {
if ( $$Storage{Type} and ( $$Storage{Type} eq 's3fs' ) ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
@ -444,7 +470,7 @@ sub delete_files {
}
};
Error($@) if $@;
}
}
if ( ! $deleted ) {
my $command = "/bin/rm -rf $storage_path/$event_path";
ZoneMinder::General::executeShellCommand( $command );
@ -453,7 +479,7 @@ sub delete_files {
if ( $event->Scheme() eq 'Deep' ) {
my $link_path = $event->LinkPath();
Debug("Deleting files for Event $$event{Id} from $storage_path/$link_path.");
Debug("Deleting link for Event $$event{Id} from $storage_path/$link_path.");
if ( $link_path ) {
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
unlink($storage_path.'/'.$link_path) or Error( "Unable to unlink '$storage_path/$link_path': $!" );
@ -467,7 +493,7 @@ sub Storage {
}
if ( ! $_[0]{Storage} ) {
$_[0]{Storage} = new ZoneMinder::Storage($_[0]{StorageId});
}
}
return $_[0]{Storage};
}
@ -488,7 +514,7 @@ sub check_for_in_filesystem {
sub age {
if ( ! $_[0]{age} ) {
if ( -e $_[0]->Path() ) {
if ( -e $_[0]->Path() ) {
# $^T is the time the program began running. -M is program start time - file modification time in days
$_[0]{age} = (time() - ($^T - ((-M $_[0]->Path() ) * 24*60*60)));
} else {
@ -564,6 +590,7 @@ sub MoveTo {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
require File::Slurp;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
@ -656,7 +683,7 @@ Debug("Files to move @files");
}
# Succeeded in copying all files, so we may now update the Event.
$$self{StorageId} = $$NewStorage{Id};
$$self{StorageId} = $$NewStorage{Id};
$$self{Storage} = $NewStorage;
$error .= $self->save();
if ( $error ) {

View File

@ -47,10 +47,8 @@ our %EXPORT_TAGS = (
getCmdFormat
runCommand
setFileOwner
getEventPath
createEventPath
createEvent
deleteEventFiles
makePath
jsonEncode
jsonDecode
@ -181,70 +179,14 @@ sub runCommand {
return( $output );
}
sub getEventPath {
my $event = shift;
my $Storage = new ZoneMinder::Storage( $$event{StorageId} );
my $event_path = join( '/',
$Storage->Path(),
$event->{MonitorId},
( $Config{ZM_USE_DEEP_STORAGE} ? strftime( "%y/%m/%d/%H/%M/%S", localtime($event->{Time}) ) : $event->{Id} ),
);
return( $event_path );
}
sub createEventPath {
#
# WARNING assumes running from events directory
#
my $event = shift;
my $Storage = new ZoneMinder::Storage( $$event{Id} );
my $eventPath = $Storage->Path() . '/'.$event->{MonitorId};
my $eventPath = $event->Path();
$event->createPath();
$event->createIdFile();
$event->createLinkPath();
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
my @startTime = localtime( $event->{StartTime} );
my @datetimeParts = ();
$datetimeParts[0] = sprintf( "%02d", $startTime[5]-100 );
$datetimeParts[1] = sprintf( "%02d", $startTime[4]+1 );
$datetimeParts[2] = sprintf( "%02d", $startTime[3] );
$datetimeParts[3] = sprintf( "%02d", $startTime[2] );
$datetimeParts[4] = sprintf( "%02d", $startTime[1] );
$datetimeParts[5] = sprintf( "%02d", $startTime[0] );
my $datePath = join('/',@datetimeParts[0..2]);
my $timePath = join('/',@datetimeParts[3..5]);
makePath( $datePath, $eventPath );
$eventPath .= '/'.$datePath;
# Create event id symlink
my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
symlink( $timePath, $idFile )
or Fatal( "Can't symlink $idFile -> $eventPath: $!" );
makePath( $timePath, $eventPath );
$eventPath .= '/'.$timePath;
setFileOwner( $idFile ); # Must come after directory has been created
# Create empty id tag file
$idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
open( my $ID_FP, ">", $idFile )
or Fatal( "Can't open $idFile: $!" );
close( $ID_FP );
setFileOwner( $idFile );
} else {
makePath( $event->{Id}, $eventPath );
$eventPath .= '/'.$event->{Id};
my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} );
open( my $ID_FP, ">", $idFile )
or Fatal( "Can't open $idFile: $!" );
close( $ID_FP );
setFileOwner( $idFile );
}
return( $eventPath );
return $eventPath;
}
use Data::Dumper;
@ -481,53 +423,6 @@ sub updateEvent {
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() );
}
sub deleteEventFiles {
#
# WARNING assumes running from events directory
#
my $event_id = shift;
my $monitor_id = shift;
$monitor_id = '*' if ( !defined($monitor_id) );
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
my $link_path = $monitor_id."/*/*/*/.".$event_id;
#Debug( "LP1:$link_path" );
my @links = glob($link_path);
#Debug( "L:".$links[0].": $!" );
if ( @links ) {
( $link_path ) = ( $links[0] =~ /^(.*)$/ ); # De-taint
#Debug( "LP2:$link_path" );
( my $day_path = $link_path ) =~ s/\.\d+//;
#Debug( "DP:$day_path" );
my $event_path = $day_path.readlink( $link_path );
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
#Debug( "EP:$event_path" );
my $command = "/bin/rm -rf ".$event_path;
#Debug( "C:$command" );
executeShellCommand( $command );
unlink( $link_path ) or Error( "Unable to unlink '$link_path': $!" );
my @path_parts = split( /\//, $event_path );
for ( my $i = int(@path_parts)-2; $i >= 1; $i-- ) {
my $delete_path = join( '/', @path_parts[0..$i] );
#Debug( "DP$i:$delete_path" );
my @has_files = glob( $delete_path."/*" );
#Debug( "HF1:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
@has_files = glob( $delete_path."/.[0-9]*" );
#Debug( "HF2:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
my $command = "/bin/rm -rf ".$delete_path;
executeShellCommand( $command );
}
}
} else {
my $command = "/bin/rm -rf $monitor_id/$event_id";
executeShellCommand( $command );
}
}
sub makePath {
my $path = shift;
my $root = shift;
@ -654,7 +549,7 @@ sub jsonDecode {
$out =~ s/=>null/=>undef/g;
$out =~ s/`/'/g;
$out =~ s/qx/qq/g;
( $out ) = $out =~ m/^({.+})$/; # Detaint and check it's a valid object syntax
( $out ) = $out =~ m/^(\{.+\})$/; # Detaint and check it's a valid object syntax
my $result = eval $out;
Fatal( $@ ) if ( $@ );
return( $result );

View File

@ -440,7 +440,7 @@ sub databaseLevel {
if ( defined($databaseLevel) ) {
$databaseLevel = $this->limit($databaseLevel);
if ( $this->{databaseLevel} != $databaseLevel ) {
if ( $databaseLevel > NOLOG and $this->{databaseLevel} <= NOLOG ) {
if ( ( $databaseLevel > NOLOG ) and ( $this->{databaseLevel} <= NOLOG ) ) {
if ( !$this->{dbh} ) {
$this->{dbh} = ZoneMinder::Database::zmDbConnect();
}
@ -560,14 +560,17 @@ sub logPrint {
if ( $level <= $this->{databaseLevel} ) {
if ( ! ( $this->{dbh} and $this->{dbh}->ping() ) ) {
$this->{sth} = undef;
# Turn this off because zDbConnect will do logging calls.
my $oldlevel = $this->{databaseLevel};
$this->{databaseLevel} = NOLOG;
if ( ! ( $this->{dbh} = ZoneMinder::Database::zmDbConnect() ) ) {
#print(STDERR "Can't log to database: ");
$this->{databaseLevel} = NOLOG;
return;
}
$this->{databaseLevel} = $oldlevel;
}
my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
my $sql = 'INSERT INTO Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, NULL )';
$this->{sth} = $this->{dbh}->prepare_cached($sql) if ! $this->{sth};
if ( !$this->{sth} ) {
$this->{databaseLevel} = NOLOG;
@ -575,13 +578,15 @@ sub logPrint {
return;
}
my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0)
, $this->{id}
, $$
, $level
, $codes{$level}
, $string
, $this->{fileName}
my $res = $this->{sth}->execute(
$seconds+($microseconds/1000000.0),
$this->{id},
($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : undef),
$$,
$level,
$codes{$level},
$string,
$this->{fileName},
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;
@ -699,6 +704,8 @@ sub Fatal( @ ) {
if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) {
$SIG{TERM}();
}
# I think if we don't disconnect we will leave sockets around in TIME_WAIT
zmDbDisconnect();
exit(-1);
}

View File

@ -53,64 +53,65 @@ $primary_key = 'Id';
#__PACKAGE__->primary_key('Id');
sub find {
shift if $_[0] eq 'ZoneMinder::Storage';
my %sql_filters = @_;
shift if $_[0] eq 'ZoneMinder::Storage';
my %sql_filters = @_;
my $sql = 'SELECT * FROM Storage';
my @sql_filters;
my @sql_values;
my $sql = 'SELECT * FROM Storage';
my @sql_filters;
my @sql_values;
if ( exists $sql_filters{Id} ) {
push @sql_filters , ' Id=? ';
push @sql_values, $sql_filters{Id};
}
if ( exists $sql_filters{Name} ) {
push @sql_filters , ' Name = ? ';
push @sql_values, $sql_filters{Name};
}
if ( exists $sql_filters{ServerId} ) {
push @sql_filters, ' Id IN ( SELECT StorageId FROM Monitors WHERE ServerId=? )';
push @sql_values, $sql_filters{ServerId};
}
if ( exists $sql_filters{Id} ) {
push @sql_filters , ' Id=? ';
push @sql_values, $sql_filters{Id};
}
if ( exists $sql_filters{Name} ) {
push @sql_filters , ' Name = ? ';
push @sql_values, $sql_filters{Name};
}
if ( exists $sql_filters{ServerId} ) {
push @sql_filters, ' ServerId = ?';
push @sql_values, $sql_filters{ServerId};
}
$sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters;
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit};
$sql .= ' WHERE ' . join(' AND ', @sql_filters) if @sql_filters;
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit};
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( @sql_values )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( @sql_values )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
my @results;
my @results;
while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter );
push @results, $filter;
} # end while
return @results;
while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter );
push @results, $filter;
} # end while
Debug("SQL: $sql returned " . @results . ' results');
return @results;
}
sub find_one {
my @results = find(@_);
return $results[0] if @results;
my @results = find(@_);
return $results[0] if @results;
}
sub Path {
if ( @_ > 1 ) {
$_[0]{Path} = $_[1];
}
if ( ! ( $_[0]{Id} or $_[0]{Path} ) ) {
$_[0]{Path} = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
}
return $_[0]{Path};
if ( @_ > 1 ) {
$_[0]{Path} = $_[1];
}
if ( ! ( $_[0]{Id} or $_[0]{Path} ) ) {
$_[0]{Path} = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS})
}
return $_[0]{Path};
} # end sub Path
sub Name {
if ( @_ > 1 ) {
$_[0]{Name} = $_[1];
}
return $_[0]{Name};
if ( @_ > 1 ) {
$_[0]{Name} = $_[1];
}
return $_[0]{Name};
} # end sub Path
sub DoDelete {

View File

@ -67,6 +67,7 @@ my $level = 1;
my $monitor_id = 0;
my $version;
my $force = 0;
my $server_id = undef;
my $storage_id = undef;
logInit();
@ -78,6 +79,7 @@ GetOptions(
level =>\$level,
'monitor_id=i' =>\$monitor_id,
report =>\$report,
'server_id=i' =>\$server_id,
'storage_id=i' =>\$storage_id,
version =>\$version
) or pod2usage(-exitstatus => -1);
@ -181,13 +183,15 @@ MAIN: while( $loop ) {
Term();
}
Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}");
} elsif ( $Config{ZM_SERVER_ID} ) {
@Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} );
} elsif ( $server_id ) {
@Storage_Areas = ZoneMinder::Storage->find( ServerId => $server_id );
if ( ! @Storage_Areas ) {
Error("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID});
Error("No Storage Area found with ServerId =" . $server_id);
Term();
}
Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name());
foreach my $Storage ( @Storage_Areas ) {
Info('Auditing ' . $Storage->Name() . ' at ' . $Storage->Path() . ' on ' . $Storage->Server()->Name() );
}
} else {
@Storage_Areas = ZoneMinder::Storage->find();
Info("Auditing All Storage Areas");
@ -260,33 +264,39 @@ MAIN: while( $loop ) {
Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
next;
}
my %event_ids_by_path;
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
Debug("Have " . @event_links . ' event links');
closedir(DIR);
my $count = 0;
foreach my $event_link ( @event_links ) {
if ( $event_link =~ /[^\d\.]/ ) {
# Event links start with a period and consist of the digits of the event id. Anything else is not an event link
my ($event_id) = $event_link =~ /^\.(\d+)$/;
if ( !$event_id ) {
Warning("Non-event link found $event_link in $day_dir, skipping");
next;
}
Debug("Checking link $event_link");
( my $event = $event_link ) =~ s/^.*\.//;
#Event path is hour/minute/sec
my $event_path = readlink($event_link);
if ( !($event_path and -e $event_path) ) {
aud_print("Event link $day_dir/$event_link does not point to valid target");
aud_print("Event link $day_dir/$event_link does not point to valid target at $event_path");
if ( confirm() ) {
( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint
unlink($event_link);
$cleaned = 1;
}
} else {
$event_ids_by_path{$event_path} = $event_id;
Debug("Checking link $event_link points to $event_path ");
my $Event = $fs_events->{$event} = new ZoneMinder::Event();
$$Event{Id} = $event;
$$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path);
$$Event{RelativePath} = join('/', $day_dir,$event_path);
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id;
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_path);
$$Event{RelativePath} = join('/', $day_dir, $event_path);
$$Event{Scheme} = 'Deep';
$Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() );
@ -303,15 +313,34 @@ MAIN: while( $loop ) {
my $event_id = undef;
my @mp4_files = glob("$event_dir/[0-9]+\-video.mp4");
if ( ! opendir(DIR, $event_dir) ) {
Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
next;
}
my @contents = readdir( DIR );
Debug("Have " . @contents . " files in $day_dir/$event_dir");
closedir(DIR);
my @mp4_files = grep( /^\d+\-video.mp4$/, @contents);
foreach my $mp4_file ( @mp4_files ) {
my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/;
if ( $id ) {
$event_id = $id;
Debug("Got event id from mp4 file $mp4_file => $event_id");
last;
}
}
if ( $event_id ) {
if ( ! $event_id ) {
# Look for .id file
my @hidden_files = grep( /^\.\d+$/, @contents);
Debug("Have " . @hidden_files . ' hidden files');
if ( @hidden_files ) {
( $event_id ) = $hidden_files[0] =~ /^.(\d+)$/;
}
}
if ( $event_id and ! $fs_events->{$event_id} ) {
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id;
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir);
@ -320,7 +349,26 @@ MAIN: while( $loop ) {
$Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() );
$Event->DiskSpace( undef );
if ( ! $event_ids_by_path{$event_dir} ) {
Warning("No event link found at ".$Event->LinkPath() ." for " . $Event->to_string());
}
} else {
if ( $event_ids_by_path{$event_dir} ) {
Debug("Have an event link, leaving dir alone.");
next;
}
my ( undef, $year, $month, $day ) = split('/', $day_dir);
$year += 2000;
my ( $hour, $minute, $second ) = split('/', $event_dir);
my $StartTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second);
my $Event = ZoneMinder::Event->find_one(
MonitorId=>$monitor_dir,
StartTime=>$StartTime,
);
if ( $Event ) {
Debug("Found event matching starttime on monitor $monitor_dir at $StartTime: " . $Event->to_string());
next;
}
aud_print("Deleting event directories with no event id information at $day_dir/$event_dir");
if ( confirm() ) {
my $command = "rm -rf $event_dir";
@ -359,7 +407,7 @@ MAIN: while( $loop ) {
}
if ( ! $$Storage{Scheme} ) {
Debug("Storage Scheme not set on $$Storage{Name}");
Error("Storage Scheme not set on $$Storage{Name}");
if ( ! chdir( $monitor_dir ) ) {
Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" );
next;
@ -382,7 +430,7 @@ MAIN: while( $loop ) {
} # if USE_DEEP_STORAGE
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir\n" );
#delete_empty_directories( $monitor_dir );
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
} # end foreach monitor
if ( $cleaned ) {
@ -473,6 +521,10 @@ MAIN: while( $loop ) {
next;
}
Debug("Event $db_event is not in fs. Should have been at ".$Event->Path());
if ( $Event->Archived() ) {
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
next;
}
if ( ! $Event->StartTime() ) {
Info("Event $$Event{Id} has no start time. deleting it.");
if ( confirm() ) {
@ -877,28 +929,50 @@ sub deleteSwapImage {
}
}
sub delete_empty_directories {
# Deletes empty sub directories of the given path.
# Does not delete the path if empty. Is not meant to be recursive.
sub delete_empty_subdirs {
my $DIR;
Debug("delete_empty_directories $_[0]");
if ( ! opendir( $DIR, $_[0] ) ) {
Error( "delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
if ( !opendir($DIR, $_[0]) ) {
Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
return;
}
my @contents = map { $_ eq '.' or $_ eq '..' ? () : $_ } readdir( $DIR );
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR );
Debug("delete_empty_subdirectories $_[0] has " . @contents .' entries:' . ( @contents < 2 ? join(',',@contents) : '' ));
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
Debug("Have " . @dirs . " dirs");
foreach ( @dirs ) {
delete_empty_directories( $_[0].'/'.$_ );
}
closedir($DIR);
}
sub delete_empty_directories {
my $DIR;
if ( !opendir($DIR, $_[0]) ) {
Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
return;
}
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR );
Debug("delete_empty_directories $_[0] has " . @contents .' entries:' . ( @contents <= 2 ? join(',',@contents) : '' ));
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
if ( @dirs ) {
Debug("Have " . @dirs . " dirs");
foreach ( @dirs ) {
delete_empty_directories( $_[0].'/'.$_ );
}
#Reload, since we may now be empty
rewinddir $DIR;
@contents = map { $_ eq '.' or $_ eq '..' ? () : $_ } readdir( $DIR );
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR );
}
closedir($DIR);
if ( ! @contents ) {
( my $dir ) = ( $_[0] =~ /^(.*)$/ );
unlink $dir;
Debug("Unlinking $dir because it's empty");
if ( ! rmdir $dir ) {
Error("Unable to unlink $dir: $!");
}
}
closedir( $DIR );
} # end sub delete_empty_directories
1;

View File

@ -272,6 +272,8 @@ sub run {
."\n"
);
# We don't want to leave killall zombies, so ignore SIGCHLD
$SIG{CHLD} = 'IGNORE';
# Tell any existing processes to die, wait 1 second between TERM and KILL
killAll(1);
@ -480,7 +482,7 @@ sub start {
logTerm();
zmDbDisconnect();
my $fd = 0;
my $fd = 3; # leave stdin,stdout,stderr open. Closing them causes problems with libx264
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close($fd++);
}

View File

@ -292,48 +292,48 @@ sub checkFilter {
last if $zm_terminate;
my $Event = new ZoneMinder::Event($$event{Id}, $event);
Debug("Checking event $event->{Id}");
Debug("Checking event $Event->{Id}");
my $delete_ok = !undef;
$dbh->ping();
if ( $filter->{AutoArchive} ) {
Info("Archiving event $event->{Id}");
Info("Archiving event $Event->{Id}");
# Do it individually to avoid locking up the table for new events
my $sql = 'UPDATE Events SET Archived = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql )
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} )
my $res = $sth->execute( $Event->{Id} )
or Error("Unable to execute '$sql': ".$dbh->errstr());
}
if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) {
if ( !$event->{Videoed} ) {
$delete_ok = undef if !generateVideo($filter, $event);
if ( !$Event->{Videoed} ) {
$delete_ok = undef if !generateVideo($filter, $Event);
}
}
if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) {
if ( !$event->{Emailed} ) {
if ( !$Event->{Emailed} ) {
$delete_ok = undef if !sendEmail($filter, $Event);
}
}
if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) {
if ( !$event->{Messaged} ) {
if ( !$Event->{Messaged} ) {
$delete_ok = undef if !sendMessage($filter, $Event);
}
}
if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) {
if ( !$event->{Uploaded} ) {
$delete_ok = undef if !uploadArchFile($filter, $event);
if ( !$Event->{Uploaded} ) {
$delete_ok = undef if !uploadArchFile($filter, $Event);
}
}
if ( $filter->{AutoExecute} ) {
if ( !$event->{Executed} ) {
$delete_ok = undef if !executeCommand($filter, $event);
if ( !$Event->{Executed} ) {
$delete_ok = undef if !executeCommand($filter, $Event);
}
}
if ( $filter->{AutoDelete} ) {
if ( $delete_ok ) {
$Event->delete();
} else {
Error("Unable toto delete event $event->{Id} as previous operations failed");
Error("Unable to delete event $Event->{Id} as previous operations failed");
}
} # end if AutoDelete
@ -364,11 +364,11 @@ sub checkFilter {
sub generateVideo {
my $filter = shift;
my $event = shift;
my $Event = shift;
my $phone = shift;
my $rate = $event->{DefaultRate}/100;
my $scale = $event->{DefaultScale}/100;
my $rate = $Event->{DefaultRate}/100;
my $scale = $Event->{DefaultScale}/100;
my $format;
my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS});
@ -393,7 +393,7 @@ sub generateVideo {
my $command = join('',
$Config{ZM_PATH_BIN},
'/zmvideo.pl -e ',
$event->{Id},
$Event->{Id},
' -r ',
$rate,
' -s ',
@ -417,8 +417,8 @@ sub generateVideo {
my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
if ( wantarray() ) {
return( $format, $output );
}
@ -467,15 +467,15 @@ sub generateImage {
sub uploadArchFile {
my $filter = shift;
my $event = shift;
my $Event = shift;
if ( ! $Config{ZM_UPLOAD_HOST} ) {
Error('Cannot upload archive as no upload host defined');
return( 0 );
}
my $archFile = $event->{MonitorName}.'-'.$event->{Id};
my $archImagePath = getEventPath($event)
my $archFile = $Event->{MonitorName}.'-'.$Event->{Id};
my $archImagePath = $Event->Path()
.'/'
.(
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
@ -540,7 +540,7 @@ sub uploadArchFile {
return( 0 );
} else {
if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) {
Info('Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP");
Info('Uploading to '.$Config{ZM_UPLOAD_HOST}.' using FTP');
my $ftp = Net::FTP->new(
$Config{ZM_UPLOAD_HOST},
Timeout=>$Config{ZM_UPLOAD_TIMEOUT},
@ -548,24 +548,24 @@ sub uploadArchFile {
Debug=>$Config{ZM_UPLOAD_DEBUG}
);
if ( !$ftp ) {
Error("Unable tocreate FTP connection: $@");
Error("Unable to create FTP connection: $@");
return 0;
}
$ftp->login($Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS})
or Error("FTP - Unable tologin");
or Error("FTP - Unable to login");
$ftp->binary()
or Error("FTP - Unable togo binary");
or Error("FTP - Unable to go binary");
$ftp->cwd($Config{ZM_UPLOAD_REM_DIR})
or Error("FTP - Unable tocwd")
or Error("FTP - Unable to cwd")
if ( $Config{ZM_UPLOAD_REM_DIR} );
$ftp->put( $archLocPath )
or Error("FTP - Unable toupload '$archLocPath'");
or Error("FTP - Unable to upload '$archLocPath'");
$ftp->quit()
or Error("FTP - Unable toquit");
or Error("FTP - Unable to quit");
} else {
my $host = $Config{ZM_UPLOAD_HOST};
$host .= ':'.$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT};
Info('Uploading to '.$host." using SFTP\n");
Info('Uploading to '.$host.' using SFTP');
my %sftpOptions = (
host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER}
($Config{ZM_UPLOAD_PASS} ? (password=>$Config{ZM_UPLOAD_PASS}) : ()),
@ -580,7 +580,7 @@ sub uploadArchFile {
$sftpOptions{more} = [@more_ssh_args];
my $sftp = Net::SFTP::Foreign->new($Config{ZM_UPLOAD_HOST}, %sftpOptions);
if ( $sftp->error ) {
Error("Unable tocreate SFTP connection: ".$sftp->error);
Error("Unable to create SFTP connection: ".$sftp->error);
return 0;
}
$sftp->setcwd($Config{ZM_UPLOAD_REM_DIR})
@ -592,9 +592,9 @@ sub uploadArchFile {
unlink($archLocPath);
my $sql = 'UPDATE Events SET Uploaded = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
}
return 1;
} # end sub uploadArchFile
@ -622,9 +622,9 @@ sub substituteTags {
WHERE EventId = ? AND Type = 'Alarm'
ORDER BY FrameId`;
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$dbh->errstr());
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
my $rows = 0;
while( my $frame = $sth->fetchrow_hashref() ) {
if ( !$first_alarm_frame ) {
@ -713,13 +713,17 @@ sub substituteTags {
}
} # end if $first_alarm_frame
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) {
if ( $attachments_ref ) {
if ( $text =~ s/%EV%//g ) {
my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) {
return undef;
if ( $$Event{DefaultVideo} ) {
push @$attachments_ref, { type=>'video/mp4', path=>join('/',$Event->Path(), $Event->DefaultVideo()) };
} elsif ( $Config{ZM_OPT_FFMPEG} ) {
my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) {
return undef;
}
push( @$attachments_ref, { type=>"video/$format", path=>$path } );
}
push( @$attachments_ref, { type=>"video/$format", path=>$path } );
}
if ( $text =~ s/%EVM%//g ) {
my ( $format, $path ) = generateVideo($filter, $Event, 1);
@ -792,7 +796,7 @@ sub sendEmail {
$ssmtp_location = qx('which ssmtp');
}
if ( !$ssmtp_location ) {
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
Debug('Unable to find ssmtp, trying MIME::Lite->send');
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
} else {
@ -824,16 +828,16 @@ sub sendEmail {
}
};
if ( $@ ) {
Error("Unable tosend email: $@");
Error("Unable to send email: $@");
return 0;
} else {
Info('Notification email sent');
}
my $sql = 'UPDATE Events SET Emailed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
return 1;
}
@ -894,7 +898,7 @@ sub sendMessage {
$ssmtp_location = qx('which ssmtp');
}
if ( !$ssmtp_location ) {
Debug('Unable tofind ssmtp, trying MIME::Lite->send');
Debug('Unable to find ssmtp, trying MIME::Lite->send');
MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send();
} else {
@ -929,29 +933,29 @@ sub sendMessage {
}
};
if ( $@ ) {
Error("Unable tosend email: $@");
Error("Unable to send email: $@");
return 0;
} else {
Info('Notification message sent');
}
my $sql = 'UPDATE Events SET Messaged = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable toprepare '$sql': ".$dbh->errstr());
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($Event->{Id})
or Fatal("Unable toexecute '$sql': ".$dbh->errstr());
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
return 1;
} # end sub sendMessage
sub executeCommand {
my $filter = shift;
my $event = shift;
my $Event = shift;
my $event_path = getEventPath($event);
my $event_path = $Event->Path();
my $command = $filter->{AutoExecuteCmd};
$command .= " $event_path";
$command = substituteTags($command, $filter, $event);
$command = substituteTags($command, $filter, $Event);
Info("Executing '$command'");
my $output = qx($command);
@ -967,7 +971,7 @@ sub executeCommand {
my $sql = 'UPDATE Events SET Executed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} )
my $res = $sth->execute( $Event->{Id} )
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
}
return( 1 );

View File

@ -44,7 +44,6 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $store_state=''; # PP - will remember state name passed
logInit();
Info("Aftere LogInit");
my $command = $ARGV[0]||'';
if ( $command eq 'version' ) {

View File

@ -53,7 +53,6 @@ use constant START_DELAY => 30; # To give everything else time to start
@EXTRA_PERL_LIB@
use ZoneMinder;
use ZoneMinder::Storage;
use POSIX;
use DBI;
use autouse 'Data::Dumper'=>qw(Dumper);
@ -72,15 +71,13 @@ sleep( START_DELAY );
my $dbh = zmDbConnect();
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
while( 1 ) {
while ( ! ( $dbh and $dbh->ping() ) ) {
Info("Reconnecting to db");
$dbh = zmDbConnect();
if ( ! ( $dbh = zmDbConnect() ) ) {
#What we do here is not that important, so just skip this interval
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
}
}
$dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr());
@ -88,7 +85,7 @@ while( 1 ) {
$dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr());
sleep( $Config{ZM_STATS_UPDATE_INTERVAL} );
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
} # end while (1)
Info( "Stats Daemon exiting\n" );

View File

@ -88,13 +88,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit();
logSetSignal();
Info( "Trigger daemon starting\n" );
Info( "Trigger daemon starting" );
my $dbh = zmDbConnect();
my $base_rin = '';
foreach my $connection ( @connections ) {
Info( "Opening connection '$connection->{name}'\n" );
Info( "Opening connection '$connection->{name}'" );
$connection->open();
}
@ -109,7 +109,7 @@ foreach my $connection ( @in_select_connections ) {
my %spawned_connections;
my %monitors;
my $monitor_reload_time = 0;
my $needsReload = 0;
my @needsReload;
loadMonitors();
$! = undef;
@ -127,14 +127,14 @@ while( 1 ) {
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
if ( $nfound > 0 ) {
Debug( "Got input from $nfound connections\n" );
Debug( "Got input from $nfound connections" );
foreach my $connection ( @in_select_connections ) {
if ( vec( $rout, $connection->fileno(), 1 ) ) {
Debug( 'Got input from connection '
.$connection->name()
.' ('
.$connection->fileno()
.")\n"
.")"
);
if ( $connection->spawns() ) {
my $new_connection = $connection->accept();
@ -143,7 +143,7 @@ while( 1 ) {
.$new_connection->fileno()
.'), '
.int(keys(%spawned_connections))
." spawned connections\n"
." spawned connections"
);
} else {
my $messages = $connection->getMessages();
@ -162,7 +162,7 @@ while( 1 ) {
.$connection->name()
.' ('
.$connection->fileno()
.")\n"
.")"
);
my $messages = $connection->getMessages();
if ( defined($messages) ) {
@ -175,7 +175,7 @@ while( 1 ) {
.$connection->fileno()
.'), '
.int(keys(%spawned_connections))
." spawned connections\n"
." spawned connections"
);
$connection->close();
}
@ -206,7 +206,7 @@ while( 1 ) {
if ( ! zmMemVerify($monitor) ) {
# Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it.
$needsReload = 1;
push @needsReload, $monitor;
next;
}
@ -217,8 +217,8 @@ while( 1 ) {
]
);
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
#print( "$monitor->{Id}: S:$state, LE:$last_event" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
if ( $state == STATE_ALARM || $state == STATE_ALERT ) {
# In alarm state
if ( !defined($monitor->{LastEvent})
@ -261,14 +261,14 @@ while( 1 ) {
}
if ( my @action_times = keys(%actions) ) {
Debug( "Checking for timed actions\n" );
Debug( "Checking for timed actions" );
my $now = time();
foreach my $action_time ( sort( grep { $_ < $now } @action_times ) ) {
Info( "Found actions expiring at $action_time\n" );
Info( "Found actions expiring at $action_time" );
foreach my $action ( @{$actions{$action_time}} ) {
my $connection = $action->{connection};
my $message = $action->{message};
Info( "Found action '$message'\n" );
Info( "Found action '$message'" );
handleMessage( $connection, $message );
}
delete( $actions{$action_time} );
@ -293,23 +293,41 @@ while( 1 ) {
}
}
# If necessary reload monitors
if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )) {
# Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) {
foreach my $monitor ( values(%monitors) ) {
# Free up any used memory handle
zmMemInvalidate( $monitor );
zmMemInvalidate( $monitor ); # Free up any used memory handle
}
loadMonitors();
$needsReload = 0;
@needsReload = (); # We just reloaded all monitors so no need reload a specific monitor
# If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed
} elsif ( @needsReload ) {
foreach my $monitor ( @needsReload ) {
loadMonitor($monitor);
}
@needsReload = ();
}
# zmDbConnect will ping and reconnect if neccessary
$dbh = zmDbConnect();
} # end while ( 1 )
Info( "Trigger daemon exiting\n" );
Info( "Trigger daemon exiting" );
exit;
sub loadMonitor {
my $monitor = shift;
Debug( "Loading monitor $monitor" );
zmMemInvalidate( $monitor );
if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
}
sub loadMonitors {
Debug( "Loading monitors\n" );
Debug( "Loading monitors" );
$monitor_reload_time = time();
my %new_monitors = ();
@ -323,14 +341,10 @@ sub loadMonitors {
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) {
# Check shared memory ok
if ( !zmMemVerify( $monitor ) ) {
zmMemInvalidate( $monitor );
next;
if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
$new_monitors{$monitor->{Id}} = $monitor;
} # end while fetchrow
%monitors = %new_monitors;
@ -348,14 +362,14 @@ sub handleMessage {
my $monitor = $monitors{$id};
if ( !$monitor ) {
Warning( "Can't find monitor '$id' for message '$message'\n" );
Warning( "Can't find monitor '$id' for message '$message'" );
return;
}
Debug( "Found monitor for id '$id'\n" );
Debug( "Found monitor for id '$id'" );
next if ( !zmMemVerify( $monitor ) );
Debug( "Handling action '$action'\n" );
Debug( "Handling action '$action'" );
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) {
my $state = $1;
my $delay = $2;
@ -366,7 +380,7 @@ sub handleMessage {
}
# Force a reload
$monitor_reload_time = 0;
Info( "Set monitor to $state\n" );
Info( "Set monitor to $state" );
if ( $delay ) {
my $action_text = $id.'|'.( ($state eq 'enable')
? 'disable'
@ -383,7 +397,7 @@ sub handleMessage {
if ( $trigger eq 'on' ) {
zmTriggerEventOn( $monitor, $score, $cause, $text );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger' '$cause'\n" );
Info( "Trigger '$trigger' '$cause'" );
if ( $delay ) {
my $action_text = $id.'|cancel';
handleDelay($delay, $connection, $action_text);
@ -396,7 +410,7 @@ sub handleMessage {
my $last_event = zmGetLastEvent( $monitor );
zmTriggerEventOff( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Trigger '$trigger'\n" );
Info( "Trigger '$trigger'" );
# Wait til it's finished
while( zmInAlarm( $monitor )
&& ($last_event == zmGetLastEvent( $monitor ))
@ -410,12 +424,12 @@ sub handleMessage {
} elsif( $action eq 'cancel' ) {
zmTriggerEventCancel( $monitor );
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
Info( "Cancelled event\n" );
Info( "Cancelled event" );
} elsif( $action eq 'show' ) {
zmTriggerShowtext( $monitor, $showtext );
Info( "Updated show text to '$showtext'\n" );
Info( "Updated show text to '$showtext'" );
} else {
Error( "Unrecognised action '$action' in message '$message'\n" );
Error( "Unrecognised action '$action' in message '$message'" );
}
} # end sub handleMessage
@ -430,7 +444,7 @@ sub handleDelay {
$action_array = $actions{$action_time} = [];
}
push( @$action_array, { connection=>$connection, message=>$action_text } );
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)" );
}
1;
__END__

View File

@ -922,6 +922,11 @@ if ( $version ) {
zmDbDisconnect();
die( "Can't find upgrade from version '$version'" );
}
# Re-enable the privacy popup after each upgrade
my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
}
zmDbDisconnect();

View File

@ -78,6 +78,11 @@ my $sth = $dbh->prepare_cached($sql)
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
while( 1 ) {
while ( ! ( $dbh and $dbh->ping() ) ) {
if ( ! ( $dbh = zmDbConnect() ) ) {
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
}
}
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal('Can\'t execute: '.$sth->errstr());
@ -98,7 +103,7 @@ while( 1 ) {
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
if ( !$capture_time ) {
my $startup_time = zmGetStartupTime($monitor);
if ( $now - $startup_time > $Config{ZM_WATCH_MAX_DELAY} ) {
if ( ( $now - $startup_time ) > $Config{ZM_WATCH_MAX_DELAY} ) {
Info(
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
@ -111,11 +116,12 @@ while( 1 ) {
}
}
if ( ! $restart ) {
my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
my $max_image_delay = (
$monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now-$capture_time;
Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay");

View File

@ -33,8 +33,7 @@ class Camera;
// Abstract base class for cameras. This is intended just to express
// common attributes
//
class Camera
{
class Camera {
protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
@ -53,49 +52,48 @@ protected:
int contrast;
bool capture;
bool record_audio;
unsigned int bytes;
unsigned int bytes;
public:
Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
virtual ~Camera();
unsigned int getId() const { return( monitor_id ); }
unsigned int getId() const { return monitor_id; }
Monitor *getMonitor();
void setMonitor( Monitor *p_monitor );
SourceType Type() const { return( type ); }
bool IsLocal() const { return( type == LOCAL_SRC ); }
bool IsRemote() const { return( type == REMOTE_SRC ); }
bool IsFile() const { return( type == FILE_SRC ); }
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
bool IscURL() const { return( type == CURL_SRC ); }
unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); }
unsigned int Colours() const { return( colours ); }
unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); }
SourceType Type() const { return type; }
bool IsLocal() const { return type == LOCAL_SRC; }
bool IsRemote() const { return type == REMOTE_SRC; }
bool IsFile() const { return type == FILE_SRC; }
bool IsFfmpeg() const { return type == FFMPEG_SRC; }
bool IsLibvlc() const { return type == LIBVLC_SRC; }
bool IscURL() const { return type == CURL_SRC; }
unsigned int Width() const { return width; }
unsigned int Height() const { return height; }
unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return subpixelorder; }
unsigned int Pixels() const { return pixels; }
unsigned int ImageSize() const { return imagesize; }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; }
virtual int Hue( int/*p_hue*/=-1 ) { return -1; }
virtual int Colour( int/*p_colour*/=-1 ) { return -1; }
virtual int Contrast( int/*p_contrast*/=-1 ) { return -1; }
bool CanCapture() const { return( capture ); }
bool CanCapture() const { return capture; }
bool SupportsNativeVideo() const {
return (type == FFMPEG_SRC);
//return (type == FFMPEG_SRC )||(type == REMOTE_SRC);
}
virtual int PrimeCapture() { return( 0 ); }
virtual int PreCapture()=0;
virtual int Capture( Image &image )=0;
virtual int PostCapture()=0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0;
virtual int Close()=0;
virtual int PrimeCapture() { return 0; }
virtual int PreCapture() = 0;
virtual int Capture(Image &image) = 0;
virtual int PostCapture() = 0;
virtual int CaptureAndRecord(Image &image, timeval recording, char* event_directory) = 0;
virtual int Close() = 0;
};
#endif // ZM_CAMERA_H

View File

@ -120,9 +120,15 @@ Event::Event(
char id_file[PATH_MAX];
char *path_ptr = path;
path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id());
// Try to make the Monitor Dir. Normally this would exist, but in odd cases might not.
if ( mkdir(path, 0755) ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
if ( storage->Scheme() == Storage::DEEP ) {
char *path_ptr = path;
path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id());
int dt_parts[6];
dt_parts[0] = stime->tm_year-100;
@ -141,9 +147,8 @@ Event::Event(
errno = 0;
if ( mkdir(path, 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
}
if ( i == 2 )
strncpy(date_path, path, sizeof(date_path));
@ -155,28 +160,24 @@ Event::Event(
if ( symlink(time_path, id_file) < 0 )
Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) {
char *path_ptr = path;
path_ptr += snprintf(
path_ptr, sizeof(path), "%s/%d/%04d-%02d-%02d",
storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
path_ptr, sizeof(path), "/%04d-%02d-%02d",
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
);
if ( mkdir(path, 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id);
if ( mkdir(path, 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
} else {
snprintf(path, sizeof(path), "%s/%d/%" PRIu64, storage->Path(), monitor->Id(), id);
path_ptr += snprintf(path, sizeof(path), "/%" PRIu64, id);
if ( mkdir(path, 0755) ) {
if ( errno != EEXIST ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
}
}
// Create empty id tag file
@ -540,7 +541,7 @@ Debug(3, "Writing video");
if ( score < 0 )
score = 0;
bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames;
bool db_frame = ( frame_type != BULK ) || (!frames) || ((frames%config.bulk_frame_interval)==0) ;
if ( db_frame ) {
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );

View File

@ -279,8 +279,8 @@ void EventStream::processCommand(const CmdMsg *msg) {
}
// If we are in single event mode and at the last frame, replay the current event
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(1, "Was in single_mode, and last frame, so jumping to 1st frame");
if ( (mode == MODE_SINGLE || mode == MODE_NONE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
curr_frame_id = 1;
} else {
Debug(1, "mode is %s, current frame is %d, frame count is %d", (mode == MODE_SINGLE ? "single" : "not single" ), curr_frame_id, event_data->frame_count );
@ -501,7 +501,7 @@ void EventStream::checkEventLoaded() {
}
if ( reload_event ) {
if ( forceEventChange || mode != MODE_SINGLE ) {
if ( forceEventChange || ( mode != MODE_SINGLE && mode != MODE_NONE ) ) {
//Info( "SQL:%s", sql );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -755,6 +755,12 @@ void EventStream::runStream() {
// commands may set send_frame to true
while(checkCommandQueue());
// Update modified time of the socket .lock file so that we can tell which ones are stale.
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
touch(sock_path_lock);
last_comm_update = now;
}
if ( step != 0 )
curr_frame_id += step;
@ -812,7 +818,7 @@ void EventStream::runStream() {
step = 0;
send_frame = true;
} else if ( !send_frame ) {
// We are paused, and doing nothing
// We are paused, not stepping and doing nothing
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > MAX_STREAM_DELAY ) {
// Send keepalive
@ -828,9 +834,11 @@ void EventStream::runStream() {
curr_stream_time = frame_data->timestamp;
if ( !paused ) {
curr_frame_id += replay_rate>0?1:-1;
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) )
curr_frame_id += (replay_rate>0) ? 1 : -1;
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
curr_frame_id = 1;
}
if ( send_frame && type != STREAM_MPEG ) {
Debug( 3, "dUs: %d", delta_us );
if ( delta_us )

View File

@ -42,7 +42,7 @@ extern "C" {
class EventStream : public StreamBase {
public:
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
typedef enum { MODE_NONE, MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
protected:
struct FrameData {

View File

@ -225,8 +225,9 @@ static void zm_log_fps(double d, const char *postfix) {
Debug(1, "%3.2f %s", d, postfix);
} else if (v % (100 * 1000)) {
Debug(1, "%1.0f %s", d, postfix);
} else
} else {
Debug(1, "%1.0fk %s", d / 1000, postfix);
}
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
@ -355,9 +356,8 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) {
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#else
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) {
dst->size = (src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1;
dst->data = reinterpret_cast<uint8_t*>(new uint64_t[dst->size]);
memcpy(dst->data, src->data, src->size );
av_new_packet(dst,src->size);
memcpy(dst->data, src->data, src->size);
dst->flags = src->flags;
return 0;
}

View File

@ -303,8 +303,8 @@ void zm_dump_codecpar ( const AVCodecParameters *par );
#define zm_av_packet_unref( packet ) av_packet_unref( packet )
#define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src )
#else
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src );
#define zm_av_packet_unref( packet ) av_free_packet( packet )
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src );
#endif
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
#define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet )

View File

@ -331,6 +331,8 @@ int FfmpegCamera::OpenFfmpeg() {
ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0);
} else if ( method == "rtpRtspHttp" ) {
ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
} else if ( method == "rtpUni" ) {
ret = av_dict_set(&opts, "rtsp_transport", "udp", 0);
} else {
Warning("Unknown method (%s)", method.c_str() );
}
@ -606,7 +608,8 @@ int FfmpegCamera::OpenFfmpeg() {
return -1;
}
mConvertContext = sws_getContext(mVideoCodecContext->width,
mConvertContext = sws_getContext(
mVideoCodecContext->width,
mVideoCodecContext->height,
mVideoCodecContext->pix_fmt,
width, height,
@ -722,7 +725,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
uint32_t video_writer_event_id = monitor->GetVideoWriterEventId();
if ( last_event_id != video_writer_event_id ) {
Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, video_writer_event_id );
Debug(2, "Have change of event. last_event(%d), our current (%d)",
last_event_id, video_writer_event_id);
if ( videoStore ) {
Info("Re-starting video storage module");
@ -731,7 +735,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
// Also don't know how much it matters for audio.
if ( packet.stream_index == mVideoStreamId ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
int ret = videoStore->writeVideoFramePacket(&packet);
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}

View File

@ -59,7 +59,7 @@ class FfmpegCamera : public Camera {
bool hwaccel;
#if HAVE_AVUTIL_HWCONTEXT_H
AVFrame *hwFrame;
DecodeContext decode;
DecodeContext decode;
#endif
// Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero.

View File

@ -649,11 +649,9 @@ void Image::Assign( const Image &image ) {
(*fptr_imgbufcpy)(buffer, image.buffer, size);
}
Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits )
{
if ( colours != ZM_COLOUR_GRAY8 )
{
Panic( "Attempt to highlight image edges when colours = %d", colours );
Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) {
if ( colours != ZM_COLOUR_GRAY8 ) {
Panic("Attempt to highlight image edges when colours = %d", colours);
}
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
@ -3319,11 +3317,11 @@ __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t*
}
/* FastBlend Neon for AArch32 */
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
__attribute__((noinline,__target__("fpu=neon")))
#endif
void neon32_armv7_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
static int8_t divider = 0;
static double current_blendpercent = 0.0;
@ -3710,11 +3708,11 @@ __attribute__((noinline)) void std_delta8_abgr(const uint8_t* col1, const uint8_
}
/* Grayscale Neon for AArch32 */
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
__attribute__((noinline,__target__("fpu=neon")))
#endif
void neon32_armv7_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) {
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
/* Q0(D0,D1) = col1+0 */
/* Q1(D2,D3) = col1+16 */
@ -3786,11 +3784,11 @@ __attribute__((noinline)) void neon64_armv8_delta8_gray8(const uint8_t* col1, co
}
/* RGB32 Neon for AArch32 */
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
__attribute__((noinline,__target__("fpu=neon")))
#endif
void neon32_armv7_delta8_rgb32(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, uint32_t multiplier) {
#if (defined(__arm__) && !defined(ZM_STRIP_NEON))
#if (defined(__arm__) && !defined(__armel__) && !defined(ZM_STRIP_NEON))
/* Q0(D0,D1) = col1+0 */
/* Q1(D2,D3) = col1+16 */

View File

@ -160,16 +160,16 @@ public:
static void Initialise();
static void Deinitialise();
inline unsigned int Width() const { return( width ); }
inline unsigned int Height() const { return( height ); }
inline unsigned int Pixels() const { return( pixels ); }
inline unsigned int Colours() const { return( colours ); }
inline unsigned int SubpixelOrder() const { return( subpixelorder ); }
inline unsigned int Size() const { return( size ); }
inline unsigned int Width() const { return width; }
inline unsigned int Height() const { return height; }
inline unsigned int Pixels() const { return pixels; }
inline unsigned int Colours() const { return colours; }
inline unsigned int SubpixelOrder() const { return subpixelorder; }
inline unsigned int Size() const { return size; }
/* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return( buffer ); }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); }
inline const uint8_t* Buffer() const { return buffer; }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[colours*((y*width)+x)]; }
/* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
@ -196,7 +196,7 @@ public:
}
inline Image &operator=( const unsigned char *new_buffer ) {
(*fptr_imgbufcpy)(buffer, new_buffer, size);
return( *this );
return *this;
}
bool ReadRaw( const char *filename );

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
#endif
bool Logger::smInitialised = false;
Logger *Logger::smInstance = 0;
Logger *Logger::smInstance = NULL;
Logger::StringMap Logger::smCodes;
Logger::IntMap Logger::smSyslogPriorities;
@ -57,10 +57,10 @@ static void subtractTime( struct timeval * const tp1, struct timeval * const tp2
void Logger::usrHandler( int sig ) {
Logger *logger = fetch();
if ( sig == SIGUSR1 )
logger->level( logger->level()+1 );
else if ( sig == SIGUSR2 )
logger->level( logger->level()-1 );
if ( sig == SIGUSR1 ) {
logger->level(logger->level()+1);
} else if ( sig == SIGUSR2 )
logger->level(logger->level()-1);
Info("Logger - Level changed to %d", logger->level());
}
@ -79,7 +79,7 @@ Logger::Logger() :
mFlush(false) {
if ( smInstance ) {
Panic( "Attempt to create second instance of Logger class" );
Panic("Attempt to create second instance of Logger class");
}
if ( !smInitialised ) {
@ -133,11 +133,11 @@ void Logger::initialise(const std::string &id, const Options &options) {
std::string tempLogFile;
if ( (envPtr = getTargettedEnv("LOG_FILE")) )
if ( (envPtr = getTargettedEnv("LOG_FILE")) ) {
tempLogFile = envPtr;
else if ( options.mLogFile.size() )
} else if ( options.mLogFile.size() ) {
tempLogFile = options.mLogFile;
else {
} else {
if ( options.mLogPath.size() ) {
mLogPath = options.mLogPath;
}
@ -169,7 +169,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog;
// Legacy
if ( (envPtr = getenv( "LOG_PRINT" )) )
if ( (envPtr = getenv("LOG_PRINT")) )
tempTerminalLevel = atoi(envPtr) ? DEBUG9 : NOLOG;
if ( (envPtr = getTargettedEnv("LOG_LEVEL")) )
@ -218,7 +218,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
mFlush = false;
if ( (envPtr = getenv("LOG_FLUSH")) ) {
mFlush = atoi( envPtr );
mFlush = atoi(envPtr);
} else if ( config.log_debug ) {
mFlush = true;
}
@ -335,6 +335,10 @@ Logger::Level Logger::level(Logger::Level level) {
mEffectiveLevel = mSyslogLevel;
if ( mEffectiveLevel > mLevel)
mEffectiveLevel = mLevel;
// DEBUG levels should flush
if ( mLevel > INFO )
mFlush = true;
}
return mLevel;
}
@ -577,12 +581,12 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
}
}
void logInit( const char *name, const Logger::Options &options ) {
void logInit(const char *name, const Logger::Options &options) {
if ( !Logger::smInstance )
Logger::smInstance = new Logger();
Logger::Options tempOptions = options;
tempOptions.mLogPath = staticConfig.PATH_LOGS;
Logger::smInstance->initialise( name, tempOptions );
Logger::smInstance->initialise(name, tempOptions);
}
void logTerm() {

View File

@ -423,15 +423,9 @@ Monitor::Monitor(
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
if ( purpose == CAPTURE ) {
struct stat statbuf;
if ( stat(monitor_dir, &statbuf) ) {
if ( errno == ENOENT || errno == ENOTDIR ) {
if ( mkdir(monitor_dir, 0755) ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
}
} else {
Warning("Error stat'ing %s, may be fatal. error is %s", monitor_dir, strerror(errno));
if ( mkdir(monitor_dir, 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
}
}
@ -609,16 +603,23 @@ bool Monitor::connect() {
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval;
}
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
}
if ( purpose == ANALYSIS ) {
if ( analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
*pre_event_buffer[i].timestamp = {0,0};
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
}
}
timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count];
last_signal = shared_data->signal;
}
Debug(3, "Success connecting");
return true;
@ -1261,6 +1262,7 @@ bool Monitor::Analyse() {
int index;
if ( adaptive_skip ) {
// I think the idea behind adaptive skip is if we are falling behind, then skip a bunch, but not all
int read_margin = shared_data->last_read_index - shared_data->last_write_index;
if ( read_margin < 0 ) read_margin += image_buffer_count;
@ -1274,7 +1276,10 @@ bool Monitor::Analyse() {
int pending_frames = shared_data->last_write_index - shared_data->last_read_index;
if ( pending_frames < 0 ) pending_frames += image_buffer_count;
Debug( 4, "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step );
Debug(4,
"ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d",
shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step
);
if ( step <= pending_frames ) {
index = (shared_data->last_read_index+step)%image_buffer_count;
} else {
@ -1294,17 +1299,17 @@ bool Monitor::Analyse() {
if ( shared_data->action ) {
// Can there be more than 1 bit set in the action? Shouldn't these be elseifs?
if ( shared_data->action & RELOAD ) {
Info( "Received reload indication at count %d", image_count );
Info("Received reload indication at count %d", image_count);
shared_data->action &= ~RELOAD;
Reload();
}
if ( shared_data->action & SUSPEND ) {
if ( Active() ) {
Info( "Received suspend indication at count %d", image_count );
Info("Received suspend indication at count %d", image_count);
shared_data->active = false;
//closeEvent();
} else {
Info( "Received suspend indication at count %d, but wasn't active", image_count );
Info("Received suspend indication at count %d, but wasn't active", image_count);
}
if ( config.max_suspend_time ) {
auto_resume_time = now.tv_sec + config.max_suspend_time;
@ -1313,7 +1318,7 @@ bool Monitor::Analyse() {
}
if ( shared_data->action & RESUME ) {
if ( Enabled() && !Active() ) {
Info( "Received resume indication at count %d", image_count );
Info("Received resume indication at count %d", image_count);
shared_data->active = true;
ref_image = *snap_image;
ready_count = image_count+(warmup_count/2);
@ -1324,24 +1329,14 @@ bool Monitor::Analyse() {
} // end if shared_data->action
if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) {
Info( "Auto resuming at count %d", image_count );
Info("Auto resuming at count %d", image_count);
shared_data->active = true;
ref_image = *snap_image;
ready_count = image_count+(warmup_count/2);
auto_resume_time = 0;
}
static bool static_undef = true;
static int last_section_mod = 0;
static bool last_signal;
if ( static_undef ) {
// Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary.
static_undef = false;
timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count];
last_signal = shared_data->signal;
}
if ( Enabled() ) {
bool signal = shared_data->signal;
@ -1394,27 +1389,24 @@ bool Monitor::Analyse() {
} else if ( signal && Active() && (function == MODECT || function == MOCORD) ) {
Event::StringSet zoneSet;
int motion_score = last_motion_score;
if ( !(image_count % (motion_frame_skip+1) ) ) {
// Get new score.
motion_score = DetectMotion(*snap_image, zoneSet);
int new_motion_score = DetectMotion(*snap_image, zoneSet);
Debug(3,
"After motion detection, last_motion_score(%d), new motion score(%d)",
last_motion_score, motion_score
last_motion_score, new_motion_score
);
// Why are we updating the last_motion_score too?
last_motion_score = motion_score;
last_motion_score = new_motion_score;
}
//int motion_score = DetectBlack( *snap_image, zoneSet );
if ( motion_score ) {
if ( last_motion_score ) {
if ( !event ) {
score += motion_score;
score += last_motion_score;
if ( cause.length() )
cause += ", ";
cause += MOTION_CAUSE;
} else {
score += motion_score;
score += last_motion_score;
}
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
@ -1436,7 +1428,7 @@ bool Monitor::Analyse() {
first_link = false;
}
}
noteSet.insert( linked_monitors[i]->Name() );
noteSet.insert(linked_monitors[i]->Name());
score += 50;
}
} else {
@ -1450,14 +1442,15 @@ bool Monitor::Analyse() {
//TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag?
if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
if ( event ) {
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
//snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
Debug(3, "Detected new event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec);
if ( section_length && ( timestamp->tv_sec >= section_length ) ) {
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
int section_mod = timestamp->tv_sec % section_length;
Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
Debug(3,
"Section length (%d) Last Section Mod(%d), new section mod(%d)",
section_length, last_section_mod, section_mod
);
if ( section_mod < last_section_mod ) {
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
//if ( state == TAPE ) {
@ -1480,7 +1473,7 @@ bool Monitor::Analyse() {
if ( ! event ) {
// Create event
event = new Event( this, *timestamp, "Continuous", noteSetMap, videoRecording );
event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording);
shared_data->last_event = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
@ -1554,6 +1547,10 @@ bool Monitor::Analyse() {
int pre_index;
int pre_event_images = pre_event_count;
if ( event ) {
// SHouldn't be able to happen because
Error("Creating new event when one exists");
}
if ( analysis_fps && pre_event_count ) {
// If analysis fps is set,
// compute the index for pre event images in the dedicated buffer
@ -1576,8 +1573,8 @@ bool Monitor::Analyse() {
else
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d) % %d",
pre_index, index, image_buffer_count, pre_event_count, image_buffer_count);
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d)",
pre_index, index, image_buffer_count, pre_event_count);
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
@ -1622,7 +1619,6 @@ bool Monitor::Analyse() {
pre_index = (pre_index + 1)%image_buffer_count;
}
}
event->AddFrames( pre_event_images, images, timestamps );
}
if ( alarm_frame_count ) {
@ -1735,6 +1731,7 @@ bool Monitor::Analyse() {
}
shared_data->state = state = IDLE;
last_section_mod = 0;
trigger_data->trigger_state = TRIGGER_CANCEL;
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) {
@ -2387,7 +2384,9 @@ int Monitor::Capture() {
}
if ( captureResult < 0 ) {
Warning("Return from Capture (%d), signal loss", captureResult);
Info("Return from Capture (%d), signal loss", captureResult);
// Tell zma to end the event. zma will reset TRIGGER
trigger_data->trigger_state = TRIGGER_OFF;
// Unable to capture image for temporary reason
// Fake a signal loss image
Rgb signalcolor;
@ -2473,19 +2472,17 @@ int Monitor::Capture() {
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
Info("%s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", name, image_count, new_fps, new_capture_bandwidth);
last_fps_time = now;
if ( new_fps != fps ) {
fps = new_fps;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u",
id, fps, new_capture_bandwidth, fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
} // end if new_fps != fps
fps = new_fps;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u",
id, fps, new_capture_bandwidth, fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
Debug(4,sql);
} // end if time has changed since last update
} // end if it might be time to report the fps
} // end if captureResult

View File

@ -254,39 +254,41 @@ protected:
VideoWriter videowriter;
std::string encoderparams;
std::vector<EncoderParameter_t> encoderparamsvec;
bool record_audio; // Whether to store the audio that we receive
bool record_audio; // Whether to store the audio that we receive
int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera
char event_prefix[64]; // The prefix applied to event names as they are created
char label_format[64]; // The format of the timestamp on the images
Coord label_coord; // The coordinates of the timestamp on the images
int label_size; // Size of the timestamp on the images
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera
char event_prefix[64]; // The prefix applied to event names as they are created
char label_format[64]; // The format of the timestamp on the images
Coord label_coord; // The coordinates of the timestamp on the images
int label_size; // Size of the timestamp on the images
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
// value is pre_event_count + alarm_frame_count - 1
int warmup_count; // How many images to process before looking for events
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
int section_length; // How long events should last in continuous modes
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int frame_skip; // How many frames to skip in continuous modes
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps; // Target framerate for video analysis
int warmup_count; // How many images to process before looking for events
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
int section_length; // How long events should last in continuous modes
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int frame_skip; // How many frames to skip in continuous modes
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps; // Target framerate for video analysis
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
int capture_delay; // How long we wait between capture frames
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not
int capture_delay; // How long we wait between capture frames
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not
bool last_signal;
double fps;
unsigned int last_camera_bytes;

View File

@ -482,7 +482,7 @@ void MonitorStream::runStream() {
swap_path = staticConfig.PATH_SWAP;
Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
if ( checkSwapPath(swap_path.c_str(), false) ) {
if ( checkSwapPath(swap_path.c_str(), true) ) {
swap_path += stringtf("/zmswap-m%d", monitor->Id());
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
@ -531,6 +531,12 @@ void MonitorStream::runStream() {
Debug(2, "Have checking command Queue for connkey: %d", connkey );
got_command = true;
}
// Update modified time of the socket .lock file so that we can tell which ones are stale.
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
touch(sock_path_lock);
last_comm_update = now;
}
}
if ( paused ) {
@ -633,7 +639,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
// Send the next frame
Monitor::Snapshot *snap = &monitor->image_buffer[index];
//Debug(2, "sending Frame.");
Debug(2, "sending Frame.");
if ( !sendFrame(snap->image, snap->timestamp) ) {
Debug(2, "sendFrame failed, quiting.");
zm_terminate = true;
@ -687,7 +693,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
} // end if buffered playback
frame_count++;
} else {
Debug(5,"Waiting for capture");
Debug(4,"Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));

View File

@ -267,7 +267,20 @@ bool StreamBase::sendTextFrame( const char *frame_text ) {
void StreamBase::openComms() {
if ( connkey > 0 ) {
unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey);
// Have to mkdir because systemd is now chrooting and the dir may not exist
if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir ZM_PATH_SOCKS %s: %s.", staticConfig.PATH_SOCKS.c_str(), strerror(errno));
}
}
unsigned int length = snprintf(
sock_path_lock,
sizeof(sock_path_lock),
"%s/zms-%06d.lock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated.");
}
@ -275,14 +288,14 @@ void StreamBase::openComms() {
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 ) {
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno));
lock_fd = 0;
} else if ( flock(lock_fd, LOCK_EX) != 0 ) {
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno));
close(lock_fd);
lock_fd = 0;
} else {
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
Debug(1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
}
sd = socket(AF_UNIX, SOCK_DGRAM, 0);
@ -292,7 +305,13 @@ void StreamBase::openComms() {
Debug(1, "Have socket %d", sd);
}
length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
length = snprintf(
loc_sock_path,
sizeof(loc_sock_path),
"%s/zms-%06ds.sock",
staticConfig.PATH_SOCKS.c_str(),
connkey
);
if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1;
@ -313,6 +332,8 @@ void StreamBase::openComms() {
snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1);
rem_addr.sun_family = AF_UNIX;
gettimeofday(&last_comm_update, NULL);
} // end if connKey > 0
Debug(2, "comms open");
} // end void StreamBase::openComms()

View File

@ -85,6 +85,7 @@ protected:
int step;
struct timeval now;
struct timeval last_comm_update;
double base_fps;
double effective_fps;

View File

@ -24,6 +24,8 @@
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
#if defined(__arm__)
#include <sys/auxv.h>
#endif
@ -414,3 +416,22 @@ Warning("ZM Compiled without LIBCURL. UriDecoding not implemented.");
#endif
}
void touch(const char *pathname) {
int fd = open(pathname,
O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
0666);
if ( fd < 0 ) {
// Couldn't open that path.
Error("Couldn't open() path \"%s in touch", pathname);
return;
}
int rc = utimensat(AT_FDCWD,
pathname,
nullptr,
0);
if ( rc ) {
Error("Couldn't utimensat() path %s in touch", pathname);
return;
}
}

View File

@ -63,5 +63,5 @@ extern unsigned int neonversion;
char *timeval_to_string( struct timeval tv );
std::string UriDecode( const std::string &encoded );
void touch( const char *pathname );
#endif // ZM_UTILS_H

View File

@ -64,7 +64,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
}
// Couldn't deduce format from filename, trying from format name
if (!oc) {
if ( !oc ) {
avformat_alloc_output_context2(&oc, NULL, format, filename);
if (!oc) {
Error(
@ -108,7 +108,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
Debug(2, "Success creating video out stream");
}
if (!video_out_ctx->codec_tag) {
if ( !video_out_ctx->codec_tag ) {
video_out_ctx->codec_tag =
av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id);
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
@ -127,9 +127,10 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
#else
video_out_stream =
avformat_new_stream(oc,(AVCodec *)(video_in_ctx->codec));
avformat_new_stream(oc, NULL);
//(AVCodec *)(video_in_ctx->codec));
//avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec));
if (!video_out_stream) {
if ( !video_out_stream ) {
Fatal("Unable to create video out stream\n");
} else {
Debug(2, "Success creating video out stream");
@ -158,6 +159,9 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
// Just copy them from the in, no reason to choose different
video_out_ctx->time_base = video_in_ctx->time_base;
if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) {
video_out_ctx->time_base = AV_TIME_BASE_Q;
}
video_out_stream->time_base = video_in_stream->time_base;
Debug(3,
@ -178,7 +182,6 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
}
Monitor::Orientation orientation = monitor->getOrientation();
Debug(3, "Have orientation");
if (orientation) {
if (orientation == Monitor::ROTATE_0) {
} else if (orientation == Monitor::ROTATE_90) {
@ -336,14 +339,17 @@ bool VideoStore::open() {
} else if (av_dict_count(opts) != 0) {
Warning("some options not set\n");
}
if (opts) av_dict_free(&opts);
if (ret < 0) {
Error("Error occurred when writing out file header to %s: %s\n",
filename, av_make_error_string(ret).c_str());
/* free the stream */
avio_closep(&oc->pb);
//avformat_free_context(oc);
return false;
}
if (opts) av_dict_free(&opts);
return true;
}
} // end VideoStore::open()
VideoStore::~VideoStore() {
@ -353,6 +359,9 @@ VideoStore::~VideoStore() {
// The codec queues data. We need to send a flush command and out
// whatever we get. Failures are not fatal.
AVPacket pkt;
// Without these we seg fault I don't know why.
pkt.data = NULL;
pkt.size = 0;
av_init_packet(&pkt);
while (1) {
@ -398,20 +407,22 @@ VideoStore::~VideoStore() {
pkt.stream_index = audio_out_stream->index;
av_interleaved_write_frame(oc, &pkt);
zm_av_packet_unref(&pkt);
} // while have buffered frames
} // end if audio_out_codec
} // while have buffered frames
} // end if audio_out_codec
// Flush Queues
Debug(1,"Flushing interleaved queues");
av_interleaved_write_frame(oc, NULL);
Debug(1,"Writing trailer");
/* Write the trailer before close */
if (int rc = av_write_trailer(oc)) {
Error("Error writing trailer %s", av_err2str(rc));
} else {
Debug(3, "Sucess Writing trailer");
Debug(3, "Success Writing trailer");
}
// WHen will be not using a file ?
// When will we not be using a file ?
if ( !(out_format->flags & AVFMT_NOFILE) ) {
/* Close the out file. */
Debug(2, "Closing");
@ -422,7 +433,7 @@ VideoStore::~VideoStore() {
} else {
Debug(3, "Not closing avio because we are not writing to a file.");
}
}
} // end if ( oc->pb )
// I wonder if we should be closing the file first.
// I also wonder if we really need to be doing all the ctx
// allocation/de-allocation constantly, or whether we can just re-use it.
@ -507,6 +518,7 @@ bool VideoStore::setup_resampler() {
}
Debug(2, "Have audio out codec");
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
// audio_out_ctx = audio_out_stream->codec;
audio_out_ctx = avcodec_alloc_context3(audio_out_codec);
@ -517,6 +529,12 @@ bool VideoStore::setup_resampler() {
}
Debug(2, "Have audio_out_ctx");
// Now copy them to the out stream
audio_out_stream = avformat_new_stream(oc, NULL);
#else
audio_out_stream = avformat_new_stream(oc, NULL);
audio_out_ctx = audio_out_stream->codec;
#endif
/* put sample parameters */
audio_out_ctx->bit_rate = audio_in_ctx->bit_rate;
@ -524,7 +542,10 @@ bool VideoStore::setup_resampler() {
audio_out_ctx->channels = audio_in_ctx->channels;
audio_out_ctx->channel_layout = audio_in_ctx->channel_layout;
audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
#else
audio_out_ctx->refcounted_frames = 1;
#endif
if (audio_out_codec->supported_samplerates) {
int found = 0;
@ -556,8 +577,6 @@ bool VideoStore::setup_resampler() {
audio_out_ctx->time_base =
(AVRational){1, audio_out_ctx->sample_rate};
// Now copy them to the out stream
audio_out_stream = avformat_new_stream(oc, audio_out_codec);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
ret = avcodec_parameters_from_context(audio_out_stream->codecpar,

View File

@ -236,7 +236,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
if ( pixel_diff_count && alarm_pixels )
pixel_diff = pixel_diff_count/alarm_pixels;
Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d",
alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
if ( alarm_pixels ) {
if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
@ -252,7 +254,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
score = (100*alarm_pixels)/polygon.Area();
if (max_alarm_pixels != 0)
score = (100*alarm_pixels)/max_alarm_pixels;
else
score = (100*alarm_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);
@ -312,7 +318,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
Debug(5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
Debug(5, "Got %d filtered pixels, need %d -> %d",
alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
if ( alarm_filter_pixels ) {
if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
@ -328,7 +335,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false;
}
score = (100*alarm_filter_pixels)/(polygon.Area());
if (max_filter_pixels != 0)
score = (100*alarm_filter_pixels)/max_filter_pixels;
else
score = (100*alarm_filter_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);
@ -438,7 +449,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
alarm_blobs--;
Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs);
Debug(6, "Merging blob %d with %d at %d,%d, %d current blobs",
bss->tag, bsm->tag, x, y, alarm_blobs);
// Clear out the old blob
bss->tag = 0;
@ -476,7 +488,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
BlobStats *bs = &blob_stats[i];
// See if we can recycle one first, only if it's at least two rows up
if ( bs->count && bs->hi_y < (int)(y-1) ) {
if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) {
if (
(min_blob_pixels && bs->count < min_blob_pixels)
||
(max_blob_pixels && bs->count > max_blob_pixels)
) {
if ( config.create_analysis_images || config.record_diag_images ) {
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
@ -589,8 +605,12 @@ bool Zone::CheckAlarms(const Image *delta_image) {
/* No blobs */
return false;
}
score = (100*alarm_blob_pixels)/(polygon.Area());
if (max_blob_pixels != 0)
score = (100*alarm_blob_pixels)/(max_blob_pixels);
else
score = (100*alarm_blob_pixels)/polygon.Area();
if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */
Debug(5, "Current score is %d", score);

View File

@ -83,7 +83,7 @@ int main( int argc, char *argv[] ) {
while (1) {
int option_index = 0;
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
int c = getopt_long(argc, argv, "m:h:v", long_options, &option_index);
if ( c == -1 ) {
break;
}
@ -144,7 +144,7 @@ int main( int argc, char *argv[] ) {
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
time_t last_analysis_update_time, cur_time;
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time( 0 );
last_analysis_update_time = time(0);
while( (!zm_terminate) && monitor->ShmValid() ) {
// Process the next image
@ -181,5 +181,5 @@ int main( int argc, char *argv[] ) {
Image::Deinitialise();
logTerm();
zmDbClose();
return( 0 );
return 0;
}

View File

@ -300,19 +300,22 @@ int main(int argc, char *argv[]) {
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) {
if ( monitors[i]->PreCapture() < 0 ) {
Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
Error("Failed to pre-capture monitor %d %d (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1;
break;
}
if ( monitors[i]->Capture() < 0 ) {
Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
Info("Failed to capture image from monitor %d %s (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1;
break;
}
if ( monitors[i]->PostCapture() < 0 ) {
Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
Error("Failed to post-capture monitor %d %s (%d/%d)",
monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
monitors[i]->Close();
result = -1;
break;

View File

@ -66,7 +66,7 @@ int main( int argc, const char *argv[] ) {
double maxfps = 10.0;
unsigned int bitrate = 100000;
unsigned int ttl = 0;
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
EventStream::StreamMode replay = EventStream::MODE_NONE;
std::string username;
std::string password;
char auth[64] = "";
@ -137,8 +137,17 @@ int main( int argc, const char *argv[] ) {
} else if ( !strcmp( name, "ttl" ) ) {
ttl = atoi(value);
} else if ( !strcmp( name, "replay" ) ) {
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
if ( !strcmp(value, "gapless") ) {
replay = EventStream::MODE_ALL_GAPLESS;
} else if ( !strcmp(value, "all") ) {
replay = EventStream::MODE_ALL;
} else if ( !strcmp(value, "none") ) {
replay = EventStream::MODE_NONE;
} else if ( !strcmp(value, "single") ) {
replay = EventStream::MODE_SINGLE;
} else {
Error("Unsupported value %s for replay, defaulting to none", value);
}
} else if ( !strcmp( name, "connkey" ) ) {
connkey = atoi(value);
} else if ( !strcmp( name, "buffer" ) ) {

View File

@ -23,7 +23,7 @@ case $i in
shift # past argument=value
;;
-d=*|--distro=*)
DISTRO="${i#*=}"
DISTROS="${i#*=}"
shift # past argument=value
;;
-i=*|--interactive=*)
@ -74,11 +74,15 @@ else
echo "Doing $TYPE build"
fi;
if [ "$DISTRO" == "" ]; then
DISTRO=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
echo "Defaulting to $DISTRO for distribution";
if [ "$DISTROS" == "" ]; then
if [ "$RELEASE" != "" ]; then
DISTROS="xenial,bionic,trusty"
else
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
fi;
echo "Defaulting to $DISTROS for distribution";
else
echo "Building for $DISTRO";
echo "Building for $DISTROS";
fi;
# Release is a special mode... it uploads to the release ppa and cannot have a snapshot
@ -93,7 +97,8 @@ if [ "$RELEASE" != "" ]; then
else
GITHUB_FORK="ZoneMinder";
fi
BRANCH="release-$RELEASE"
# We use a tag instead of a branch atm.
BRANCH=$RELEASE
else
if [ "$GITHUB_FORK" == "" ]; then
echo "Defaulting to ZoneMinder upstream git"
@ -115,6 +120,18 @@ else
fi;
fi
PPA="";
if [ "$RELEASE" != "" ]; then
# 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"
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
else
if [ "$BRANCH" == "" ]; then
PPA="ppa:iconnor/zoneminder-master";
else
PPA="ppa:iconnor/zoneminder-$BRANCH";
fi;
fi;
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead.
if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then
@ -153,6 +170,11 @@ if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
fi;
DIRECTORY="zoneminder_$VERSION";
if [ -d "$DIRECTORY.orig" ]; then
echo "$DIRECTORY.orig already exists. Please delete it."
exit 0;
fi;
echo "Doing $TYPE release $DIRECTORY";
mv "${GITHUB_FORK}_zoneminder_release" "$DIRECTORY.orig";
if [ $? -ne 0 ]; then
@ -160,102 +182,150 @@ if [ $? -ne 0 ]; then
echo "Setting up build dir failed.";
exit $?;
fi;
cd "$DIRECTORY.orig";
# Init submodules
git submodule init
git submodule update --init --recursive
if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then
mv distros/ubuntu1204 debian
else
if [ "$DISTRO" == "wheezy" ]; then
mv distros/debian debian
else
mv distros/ubuntu1604 debian
fi;
fi;
if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then
AUTHOR="$DEBFULLNAME <$DEBEMAIL>"
else
if [ -z `hostname -d` ] ; then
AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`.local>"
else
AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`>"
fi
fi
if [ "$URGENCY" = "" ]; then
URGENCY="medium"
fi;
if [ "$SNAPSHOT" == "stable" ]; then
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
* Release $VERSION
-- $AUTHOR $DATE
EOF
cat <<EOF > debian/NEWS
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
* Release $VERSION
-- $AUTHOR $DATE
EOF
else
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
*
-- $AUTHOR $DATE
EOF
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
*
-- $AUTHOR $DATE
EOF
fi;
# Cleanup
rm -rf .git
rm .gitignore
cd ../
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
cd $DIRECTORY.orig
if [ $TYPE == "binary" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir ./debian/control
echo "Status: $?"
DEBUILD=debuild
else
if [ $TYPE == "local" ]; then
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
fi;
IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
echo "Generating package for $DISTRO";
cd $DIRECTORY.orig
if [ -e "debian" ]; then
rm -rf debian
fi;
# Generate Changlog
if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then
cp -Rpd distros/ubuntu1204 debian
else
if [ "$DISTRO" == "wheezy" ]; then
cp -Rpd distros/debian debian
else
cp -Rpd distros/ubuntu1604 debian
fi;
fi;
if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then
AUTHOR="$DEBFULLNAME <$DEBEMAIL>"
else
if [ -z `hostname -d` ] ; then
AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`.local>"
else
AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`>"
fi
fi
if [ "$URGENCY" = "" ]; then
URGENCY="medium"
fi;
if [ "$SNAPSHOT" == "stable" ]; then
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
* Release $VERSION
-- $AUTHOR $DATE
EOF
cat <<EOF > debian/NEWS
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
* Release $VERSION
-- $AUTHOR $DATE
EOF
else
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
*
-- $AUTHOR $DATE
EOF
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
*
-- $AUTHOR $DATE
EOF
fi;
if [ $TYPE == "binary" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir ./debian/control
echo "Status: $?"
DEBUILD="debuild -i -us -uc -b"
else
# Source build, don't need build depends.
DEBUILD="debuild -S -sa"
DEBUILD=debuild
else
if [ $TYPE == "local" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir ./debian/control
echo "Status: $?"
DEBUILD="debuild -i -us -uc -b"
else
# Source build, don't need build depends.
DEBUILD="debuild -S -sa"
fi;
fi;
if [ "$DEBSIGN_KEYID" != "" ]; then
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
fi
eval $DEBUILD
if [ $? -ne 0 ]; then
echo "Error status code is: $?"
echo "Build failed.";
exit $?;
fi;
fi;
if [ "$DEBSIGN_KEYID" != "" ]; then
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
fi
$DEBUILD
if [ $? -ne 0 ]; then
echo "Error status code is: $?"
echo "Build failed.";
exit $?;
fi;
cd ../
cd ../
if [ $TYPE == "binary" ]; then
if [ "$INTERACTIVE" != "no" ]; then
read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)"
if [[ $REPLY == [yY] ]]; then
sudo dpkg -i $DIRECTORY*.deb
fi;
read -p "Do you want to upload this binary to zmrepo? (y/N)"
if [[ $REPLY == [yY] ]]; 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/"
else
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/"
else
scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
fi;
fi;
fi;
fi;
else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
dput="Y";
if [ "$INTERACTIVE" != "no" ]; then
read -p "Ready to dput $SC to $PPA ? Y/N...";
if [[ "$REPLY" == [yY] ]]; then
dput $PPA $SC
fi;
fi;
fi;
done; # foreach distro
if [ "$INTERACTIVE" != "no" ]; then
read -p "Do you want to keep the checked out version of Zoneminder (incase you want to modify it later) [y/N]"
[[ $REPLY == [yY] ]] && { mv "$DIRECTORY.orig" zoneminder_release; echo "The checked out copy is preserved in zoneminder_release"; } || { rm -fr "$DIRECTORY.orig"; echo "The checked out copy has been deleted"; }
@ -264,45 +334,4 @@ else
rm -fr "$DIRECTORY.orig"; echo "The checked out copy has been deleted";
fi
if [ $TYPE == "binary" ]; then
if [ "$INTERACTIVE" != "no" ]; then
read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)"
if [[ $REPLY == [yY] ]]; then
sudo dpkg -i $DIRECTORY*.deb
fi;
read -p "Do you want to upload this binary to zmrepo? (y/N)"
if [[ $REPLY == [yY] ]]; 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/"
else
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/"
else
scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
fi;
fi;
fi;
fi;
else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
PPA="";
if [ "$RELEASE" != "" ]; then
PPA="ppa:iconnor/zoneminder";
else
if [ "$BRANCH" == "" ]; then
PPA="ppa:iconnor/zoneminder-master";
else
PPA="ppa:iconnor/zoneminder-$BRANCH";
fi;
fi;
dput="Y";
if [ "$INTERACTIVE" != "no" ]; then
read -p "Ready to dput $SC to $PPA ? Y/N...";
if [[ "$REPLY" == [yY] ]]; then
dput $PPA $SC
fi;
fi;
fi;

View File

@ -154,7 +154,7 @@ movecrud () {
echo "Unpacking CakePHP-Enum-Behavior plugin..."
tar -xzf build/cakephp-enum-behavior-${CEBVER}.tar.gz
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
mv -f crud-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
fi
}

View File

@ -14,9 +14,17 @@ $| = 1;
my @monitors;
my $dbh = zmDbConnect();
my $sql = "SELECT * FROM Monitors";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() );
my $sql = "SELECT * FROM Monitors
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' )
;
my $sth = $dbh->prepare_cached( $sql )
or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or die( "Can't execute '$sql': ".$sth->errstr() );
while ( my $monitor = $sth->fetchrow_hashref() ) {
push( @monitors, $monitor );
@ -24,6 +32,12 @@ while ( my $monitor = $sth->fetchrow_hashref() ) {
while (1) {
foreach my $monitor (@monitors) {
# Check shared memory ok
if ( !zmMemVerify( $monitor ) ) {
zmMemInvalidate( $monitor );
next;
}
my $monitorState = zmGetMonitorState($monitor);
printState($monitor->{Id}, $monitor->{Name}, $monitorState);
}

View File

@ -1 +1 @@
1.31.46
1.32.1

View File

@ -93,7 +93,7 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
foreach ( $available_streams as &$stream ) {
# check for existence in db.
$stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') );
$monitors = Monitor::find_all( array('Path'=>$stream['url']) );
$monitors = Monitor::find( array('Path'=>$stream['url']) );
if ( count($monitors) ) {
Info("Found monitors matching " . $stream['url'] );
$stream['Monitor'] = $monitors[0];
@ -135,7 +135,7 @@ if ( canEdit( 'Monitors' ) ) {
if ( 0 ) {
// Shortcut test
$monitors = Monitor::find_all( array( 'Path'=>$_REQUEST['url'] ) );
$monitors = Monitor::find( array('Path'=>$_REQUEST['url']) );
if ( count( $monitors ) ) {
Info("Monitor found for " . $_REQUEST['url']);
$url_bits['url'] = $_REQUEST['url'];

View File

@ -33,7 +33,7 @@ switch ( $_REQUEST['task'] ) {
if ( !canView('System') )
ajaxError('Insufficient permissions to view log entries');
$servers = Server::find_all();
$servers = Server::find();
$servers_by_Id = array();
# There is probably a better way to do this.
foreach ( $servers as $server ) {
@ -112,7 +112,7 @@ switch ( $_REQUEST['task'] ) {
} else if ( $field == 'ServerId' ) {
$options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : '';
} else if ( isset($log[$field]) ) {
$options[$field][$log[$field]] = $log[$field];
$options[$field][$log[$field]] = $value;
}
}
$logs[] = $log;
@ -125,7 +125,7 @@ switch ( $_REQUEST['task'] ) {
'available' => isset($available) ? $available : $total,
'logs' => $logs,
'state' => logState(),
'options' => $options
'options' => $options,
) );
break;
}
@ -153,7 +153,7 @@ switch ( $_REQUEST['task'] ) {
}
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc';
$servers = Server::find_all();
$servers = Server::find();
$servers_by_Id = array();
# There is probably a better way to do this.
foreach ( $servers as $server ) {

View File

@ -383,7 +383,7 @@ function getNearEvents() {
parseSort();
if ( $user['MonitorIds'] )
$midSql = ' and MonitorId in ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')';
$midSql = ' AND MonitorId IN ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')';
else
$midSql = '';
@ -392,32 +392,40 @@ function getNearEvents() {
$sortOrder = 'asc';
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc') . ' LIMIT 2';
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
$prevEvent = dbFetchNext( $result );
break;
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id<'.$event['Id'] . " ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc');
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id DESC';
}
$sql .= ' LIMIT 1';
$result = dbQuery( $sql );
$prevEvent = dbFetchNext( $result );
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn $sortOrder LIMIT 2";
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
$nextEvent = dbFetchNext( $result );
break;
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id>'.$event['Id'] . " ORDER BY $sortColumn $sortOrder";
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id ASC';
}
$sql .= ' LIMIT 1';
$result = dbQuery( $sql );
$nextEvent = dbFetchNext( $result );
$result = array( 'EventId'=>$eventId );
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id']));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id']));
return( $result );
if ( $prevEvent ) {
$result['PrevEventId'] = $prevEvent['Id'];
$result['PrevEventStartTime'] = $prevEvent['StartTime'];
$result['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']);
} else {
$result['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0;
}
if ( $nextEvent ) {
$result['NextEventId'] = $nextEvent['Id'];
$result['NextEventStartTime'] = $nextEvent['StartTime'];
$result['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']);
} else {
$result['NextEventId'] = $result['NextEventStartTime'] = $result['NextEventDefVideoPath'] = 0;
}
return $result;
}
?>

View File

@ -101,27 +101,25 @@ class HostController extends AppController {
$this->loadModel('Monitor');
// If $mid is passed, see if it is valid
if ($mid) {
if (!$this->Monitor->exists($mid)) {
if ( $mid ) {
if ( !$this->Monitor->exists($mid) ) {
throw new NotFoundException(__('Invalid monitor'));
}
}
$zm_dir_events = $this->Config->find('list', array(
'conditions' => array('Name' => 'ZM_DIR_EVENTS'),
'fields' => array('Name', 'Value')
));
$zm_dir_events = $zm_dir_events['ZM_DIR_EVENTS' ];
$zm_dir_events = ZM_DIR_EVENTS;
// Test to see if $zm_dir_events is relative or absolute
if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) {
#if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) {
if ( substr($zm_dir_events, 0, 1) != '/' ) {
// relative - so add the full path
$zm_dir_events = Configure::read('ZM_PATH_WEB') . '/' . $zm_dir_events;
$zm_dir_events = ZM_PATH_WEB . '/' . $zm_dir_events;
}
if ($mid) {
if ( $mid ) {
// Get disk usage for $mid
$usage = shell_exec ("du -sh0 $zm_dir_events/$mid | awk '{print $1}'");
Logger::Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
$usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'");
} else {
$monitors = $this->Monitor->find('all', array(
'fields' => array('Id', 'Name', 'WebColour')

View File

@ -0,0 +1,157 @@
<?php
App::uses('AppController', 'Controller');
/**
* Storage Controller
*
* @property Storage $Storage
* @property PaginatorComponent $Paginator
*/
class StorageController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('Paginator', 'RequestHandler');
public function beforeFilter() {
parent::beforeFilter();
global $user;
$canView = (!$user) || ($user['System'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
}
/**
* index method
*
* @return void
*/
public function index() {
$this->Storage->recursive = 0;
$options = '';
$storage_areas = $this->Storage->find('all',$options);
$this->set(array(
'storage' => $storage_areas,
'_serialize' => array('storage')
));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
$this->Storage->recursive = 0;
if (!$this->Storage->exists($id)) {
throw new NotFoundException(__('Invalid storage area'));
}
$restricted = '';
$options = array('conditions' => array(
array('Storage.' . $this->Storage->primaryKey => $id),
$restricted
)
);
$storage = $this->Storage->find('first', $options);
$this->set(array(
'storage' => $storage,
'_serialize' => array('storage')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ( $this->request->is('post') ) {
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Storage->create();
if ( $this->Storage->save($this->request->data) ) {
# Might be nice to send it a start request
#$this->daemonControl($this->Storage->id, 'start', $this->request->data);
return $this->flash(__('The storage area has been saved.'), array('action' => 'index'));
}
}
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->Storage->id = $id;
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
if ( !$this->Storage->exists($id) ) {
throw new NotFoundException(__('Invalid storage area'));
}
if ( $this->Storage->save($this->request->data) ) {
$message = 'Saved';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
// - restart this storage area after change
#$this->daemonControl($this->Storage->id, 'restart', $this->request->data);
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Storage->id = $id;
if ( !$this->Storage->exists() ) {
throw new NotFoundException(__('Invalid storage area'));
}
$this->request->allowMethod('post', 'delete');
#$this->daemonControl($this->Storage->id, 'stop');
if ( $this->Storage->delete() ) {
return $this->flash(__('The storage area has been deleted.'), array('action' => 'index'));
} else {
return $this->flash(__('The storage area could not be deleted. Please, try again.'), array('action' => 'index'));
}
}
}

View File

@ -110,7 +110,7 @@ class Monitor extends AppModel {
);
public $actsAs = array(
'CakePHP-Enum-Behavior.Enum' => array(
'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'),
'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite'),
'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'),
'Orientation' => array('0','90','180','270','hori','vert'),
'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'),

View File

@ -19,7 +19,7 @@
.overlayHeader {
float: left;
background-color: #dddddd;
background-color: #853131;
width: 100%;
border-bottom: 1px solid #666666;
color: black;

31
web/fonts/license.md Normal file
View File

@ -0,0 +1,31 @@
ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file
### Material Design icons
Origin: https://github.com/google/material-design-icons
License: Apache 2.0 (https://github.com/google/material-design-icons/blob/master/LICENSE)
### Glyphicon halflings font
Origin: http://www.glyphicons.com/
License: MIT, As clarified below (http://www.glyphicons.com/license/)
```
License for GLYPHICONS Halflings in Bootstrap
GLYPHICONS Halflings font is also released as an extension of a Bootstrap www.getbootstrap.com for free and
it is released under the same license as Bootstrap. While you are not required to include attribution on your
Bootstrap-based projects, I would certainly appreciate any form of support, even a nice Tweet is enough.
Of course if you want, you can say thank you and support me by buying more icons on GLYPHICONS.com.
Jan Kovařík
```
ZoneMinder uses Bootstrap for UI and qualifies as a Bootstrap-based project.
Bootstrap is MIT licensed (https://github.com/twbs/bootstrap/blob/v4.1.3/LICENSE)

View File

@ -166,8 +166,7 @@ private $defaults = array(
}
}
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Controls ';
$values = array();
@ -189,15 +188,37 @@ private $defaults = array(
}
$sql .= implode(' AND ', $fields );
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line");
return;
}
}
}
$controls = array();
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
$controls[] = $obj;
}
return $filters;
return $controls;
}
public static function find_one( $parameters = array() ) {
$results = Control::find( $parameters, array('limit'=>1) );
if ( ! sizeof($results) ) {
return;
}
return $results[0];
}
public function save( $new_values = null ) {

View File

@ -84,7 +84,12 @@ class Event {
}
public function Monitor() {
return new Monitor( isset($this->{'MonitorId'}) ? $this->{'MonitorId'} : NULL );
if ( isset($this->{'MonitorId'}) ) {
$Monitor = Monitor::find_one(array('Id'=>$this->{'MonitorId'}));
if ( $Monitor )
return $Monitor;
}
return new Monitor();
}
public function __call( $fn, array $args){
@ -94,10 +99,14 @@ class Event {
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning("Unknown function call Event->$fn from $file:$line");
$backTrace = debug_backtrace();
$file = $backTrace[0]['file'];
$line = $backTrace[0]['line'];
Warning("Unknown function call Event->$fn from $file:$line");
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning("Unknown function call Event->$fn from $file:$line");
Warning(print_r( $this, true ));
}
}
@ -246,7 +255,7 @@ class Event {
if ( is_null($new) or ( $new != '' ) ) {
$this->{'DiskSpace'} = $new;
}
if ( null === $this->{'DiskSpace'} ) {
if ( (!array_key_exists('DiskSpace',$this)) or (null === $this->{'DiskSpace'}) ) {
$this->{'DiskSpace'} = folder_size($this->Path());
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
}
@ -486,7 +495,7 @@ class Event {
isset($event_cache[$parameters['Id']]) ) {
return $event_cache[$parameters['Id']];
}
$results = Event::find_all( $parameters, $options );
$results = Event::find( $parameters, $options );
if ( count($results) > 1 ) {
Error("Event Returned more than 1");
return $results[0];
@ -497,8 +506,7 @@ class Event {
}
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Events ';
$values = array();
@ -520,13 +528,27 @@ class Event {
}
$sql .= implode(' AND ', $fields );
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Event::find from $file:$line");
return array();
}
}
}
$filters = array();
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Event');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
$results = $result->fetchALL();
foreach ( $results as $row ) {
$filters[] = new Event($row);
}
return $filters;
}

View File

@ -11,6 +11,7 @@ public $defaults = array(
'AutoDelete' => 0,
'AutoArchive' => 0,
'AutoVideo' => 0,
'AutoUpload' => 0,
'AutoMessage' => 0,
'AutoMove' => 0,
'AutoMoveTo' => 0,
@ -26,12 +27,12 @@ public $defaults = array(
public function __construct( $IdOrRow=NULL ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Filters WHERE Id=?', NULL, array( $IdOrRow ) );
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Filters WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error('Unable to load Filter record for Id=' . $IdOrRow );
Error('Unable to load Filter record for Id=' . $IdOrRow);
}
} elseif ( is_array( $IdOrRow ) ) {
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
} else {
$backTrace = debug_backtrace();
@ -47,8 +48,8 @@ public $defaults = array(
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
if ( array_key_exists( 'Query', $this ) and $this->{'Query'} ) {
$this->{'Query'} = jsonDecode( $this->{'Query'} );
if ( array_key_exists('Query', $this) and $this->{'Query'} ) {
$this->{'Query'} = jsonDecode($this->{'Query'});
} else {
$this->{'Query'} = array();
}
@ -111,18 +112,62 @@ public $defaults = array(
return $this->defaults{'limit'};
}
public static function find_all() {
public static function find( $parameters = null, $options = null ) {
$filters = array();
$result = dbQuery( 'SELECT * FROM Filters ORDER BY Name');
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter' );
$sql = 'SELECT * FROM Filters ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields);
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Filter::find from $file:$line");
return array();
}
}
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
} # end find()
public static function find_one( $parameters = array() ) {
$results = Filter::find($parameters, array('limit'=>1));
if ( ! sizeof($results) ) {
return;
}
return $results[0];
} # end find_one()
public function delete() {
dbQuery( 'DELETE FROM Filters WHERE Id = ?', array($this->{'Id'}) );
dbQuery('DELETE FROM Filters WHERE Id = ?', array($this->{'Id'}));
} # end function delete()
public function set( $data ) {
@ -140,9 +185,7 @@ public $defaults = array(
$this->{$k} = $v;
}
}
}
} # end class
} # end function set
} # end class Filter
?>

View File

@ -55,7 +55,7 @@ class Frame {
#return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'}.'&show='.$show.'&filename='.$this->Event()->MonitorId().'_'.$this->{'EventId'}.'_'.$this->{'FrameId'}.'.jpg';
} // end function getImageSrc
public static function find( $parameters = array(), $limit = NULL ) {
public static function find( $parameters = array(), $options = NULL ) {
$sql = 'SELECT * FROM Frames';
$values = array();
if ( sizeof($parameters) ) {
@ -65,17 +65,23 @@ class Frame {
) );
$values = array_values( $parameters );
}
if ( $limit ) {
if ( is_integer( $limit ) or ctype_digit( $limit ) ) {
$sql .= ' LIMIT ' . $limit;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit($limit) passed to Frame::find from $file:$line");
return array();
}
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Frame::find from $file:$line");
return array();
}
}
}
$results = dbFetchAll($sql, NULL, $values);
if ( $results ) {
return array_map( function($id){ return new Frame($id); }, $results );
@ -83,8 +89,9 @@ class Frame {
return array();
}
public static function find_one( $parameters = array() ) {
$results = Frame::find( $parameters, 1 );
public static function find_one( $parameters = array(), $options = null ) {
$options['limit'] = 1;
$results = Frame::find($parameters, $options);
if ( ! sizeof($results) ) {
return;
}

View File

@ -58,27 +58,7 @@ class Group {
}
}
public static function find_one($parameters = null, $options = null) {
global $group_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($group_cache[$parameters['Id']]) ) {
return $group_cache[$parameters['Id']];
}
$results = Group::find_all($parameters, $options);
if ( count($results) > 1 ) {
Error("Group::find_one Returned more than 1");
return $results[0];
} else if ( count($results) ) {
return $results[0];
} else {
return null;
}
}
public static function find_all( $parameters = null ) {
$filters = array();
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Groups ';
$values = array();
@ -90,22 +70,57 @@ class Group {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
$sql .= ' ORDER BY Name';
$sql .= implode(' AND ', $fields);
} # end if parameters
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Group::find from $file:$line");
return array();
}
}
} # end if options
$groups = array();
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Group');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
$groups[] = $obj;
}
return $groups;
} # end find()
public static function find_one($parameters = null, $options = null) {
global $group_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($group_cache[$parameters['Id']]) ) {
return $group_cache[$parameters['Id']];
}
$results = Group::find($parameters, $options);
if ( count($results) > 1 ) {
Error("Group::find_one Returned more than 1");
return $results[0];
} else if ( count($results) ) {
return $results[0];
} else {
return null;
}
return $filters;
}
public function delete() {
@ -182,7 +197,7 @@ class Group {
public static function get_dropdown_options() {
$Groups = array();
foreach ( Group::find_all( ) as $Group ) {
foreach ( Group::find( ) as $Group ) {
$Groups[$Group->Id()] = $Group;
}

View File

@ -2,6 +2,8 @@
require_once('database.php');
require_once('Server.php');
$monitor_cache = array();
class Monitor {
private $defaults = array(
@ -150,21 +152,27 @@ private $control_fields = array(
}
if ( $this->{'Controllable'} ) {
$s = dbFetchOne('SELECT * FROM Controls WHERE Id=?', NULL, array($this->{'ControlId'}) );
foreach ($s as $k => $v) {
if ( $k == 'Id' ) {
continue;
# The reason for these is that the name overlaps Monitor fields.
} else if ( $k == 'Protocol' ) {
$this->{'ControlProtocol'} = $v;
} else if ( $k == 'Name' ) {
$this->{'ControlName'} = $v;
} else if ( $k == 'Type' ) {
$this->{'ControlType'} = $v;
} else {
$this->{$k} = $v;
if ( $s ) {
foreach ($s as $k => $v) {
if ( $k == 'Id' ) {
continue;
# The reason for these is that the name overlaps Monitor fields.
} else if ( $k == 'Protocol' ) {
$this->{'ControlProtocol'} = $v;
} else if ( $k == 'Name' ) {
$this->{'ControlName'} = $v;
} else if ( $k == 'Type' ) {
$this->{'ControlType'} = $v;
} else {
$this->{$k} = $v;
}
}
} else {
Warning('No Controls found for monitor '.$this->{'Id'} . ' ' . $this->{'Name'}.' althrough it is marked as controllable');
}
}
global $monitor_cache;
$monitor_cache[$row['Id']] = $this;
} else {
Error('No row for Monitor ' . $IdOrRow);
@ -278,8 +286,7 @@ private $control_fields = array(
} # end if method_exists
} # end foreach $data as $k=>$v
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Monitors ';
$values = array();
@ -289,9 +296,9 @@ private $control_fields = array(
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
} else if ( is_array($value) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
} else {
@ -299,18 +306,47 @@ private $control_fields = array(
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
$sql .= implode(' AND ', $fields);
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line");
return array();
}
}
}
$monitors = array();
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Monitor');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
$results = $result->fetchALL();
foreach ( $results as $row ) {
$monitors[] = new Monitor($row);
}
return $filters;
}
return $monitors;
} # end find
public static function find_one( $parameters = array() ) {
global $monitor_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($monitor_cache[$parameters['Id']]) ) {
return $monitor_cache[$parameters['Id']];
}
$results = Monitor::find( $parameters, array('limit'=>1) );
if ( ! sizeof($results) ) {
return;
}
return $results[0];
} # end find_one
public function save($new_values = null) {
@ -489,8 +525,12 @@ private $control_fields = array(
$source = preg_replace( '/^.*\//', '', $this->{'Path'} );
} elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) {
$url_parts = parse_url( $this->{'Path'} );
if ( ZM_WEB_FILTER_SOURCE == "Hostname" ) { # Filter out everything but the hostname
$source = $url_parts['host'];
if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { # Filter out everything but the hostname
if ( isset($url_parts['host']) ) {
$source = $url_parts['host'];
} else {
$source = $this->{'Path'};
}
} elseif ( ZM_WEB_FILTER_SOURCE == "NoCredentials" ) { # Filter out sensitive and common items
unset($url_parts['user']);
unset($url_parts['pass']);
@ -509,5 +549,10 @@ private $control_fields = array(
}
return $source;
} // end function Source
public function Url() {
return $this->Server()->Url( ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null );
}
} // end class Monitor
?>

View File

@ -1,6 +1,8 @@
<?php
require_once('database.php');
$server_cache = array();
class Server {
private $defaults = array(
'Id' => null,
@ -10,15 +12,18 @@ class Server {
'zmstats' => 1,
'zmtrigger' => 0,
);
public function __construct( $IdOrRow = NULL ) {
global $server_cache;
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or ctype_digit( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error("Unable to load Server record for Id=" . $IdOrRow );
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Servers WHERE Id=?', NULL, array($IdOrRow));
if ( !$row ) {
Error("Unable to load Server record for Id=" . $IdOrRow);
}
} elseif ( is_array( $IdOrRow ) ) {
} elseif ( is_array($IdOrRow) ) {
$row = $IdOrRow;
}
} # end if isset($IdOrRow)
@ -26,52 +31,25 @@ class Server {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
$server_cache[$row['Id']] = $this;
} else {
$this->{'Name'} = '';
$this->{'Hostname'} = '';
}
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Servers ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
public function Url() {
public function Url( $port = null ) {
$url = ZM_BASE_PROTOCOL . '://';
if ( $this->Id() ) {
return ZM_BASE_PROTOCOL . '://'. $this->Hostname();
$url .= $this->Hostname();
} else {
return ZM_BASE_PROTOCOL . '://'. $_SERVER['SERVER_NAME'];
return '';
$url .= $_SERVER['SERVER_NAME'];
}
if ( $port ) {
$url .= ':'.$port;
}
$url .= $_SERVER['PHP_SELF'];
return $url;
}
public function Hostname() {
if ( isset( $this->{'Hostname'} ) and ( $this->{'Hostname'} != '' ) ) {
@ -96,35 +74,62 @@ class Server {
}
}
}
public static function find( $parameters = array(), $limit = NULL ) {
$sql = 'SELECT * FROM Servers';
public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Servers ';
$values = array();
if ( sizeof($parameters) ) {
$sql .= ' WHERE ' . implode( ' AND ', array_map(
function($v){ return $v.'=?'; },
array_keys( $parameters )
) );
$values = array_values( $parameters );
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Server::find from $file:$line");
return array();
}
}
}
if ( is_integer( $limit ) or ctype_digit( $limit ) ) {
$sql .= ' LIMIT ' . $limit;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit($limit) passed to Server::find from $file:$line");
return;
}
$results = dbFetchAll( $sql, NULL, $values );
if ( $results ) {
return array_map( function($id){ return new Server($id); }, $results );
}
return array();
}
public static function find_one( $parameters = array() ) {
$results = Server::find( $parameters, 1 );
if ( ! sizeof( $results ) ) {
global $server_cache;
if (
( count($parameters) == 1 ) and
isset($parameters['Id']) and
isset($server_cache[$parameters['Id']]) ) {
return $server_cache[$parameters['Id']];
}
$results = Server::find( $parameters, array('limit'=>1) );
if ( ! sizeof($results) ) {
return;
}
return $results[0];

View File

@ -3,15 +3,27 @@ require_once('database.php');
$storage_cache = array();
class Storage {
private $defaults = array(
'Id' => null,
'Path' => '',
'Name' => '',
'Type' => 'local',
'Url' => '',
'DiskSpace' => null,
'Scheme' => 'Medium',
'ServerId' => 0,
'DoDelete' => 1,
);
public function __construct( $IdOrRow = NULL ) {
global $storage_cache;
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array( $IdOrRow ) );
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) {
Error("Unable to load Storage record for Id=" . $IdOrRow );
Error('Unable to load Storage record for Id=' . $IdOrRow);
}
} else if ( is_array($IdOrRow) ) {
$row = $IdOrRow;
@ -40,7 +52,6 @@ class Storage {
$this->{'Path'} = ZM_DIR_EVENTS;
}
return $this->{'Path'};
}
return $this->{'Name'};
}
@ -53,19 +64,25 @@ class Storage {
return $this->{'Name'};
}
public function __call( $fn, array $args= NULL){
if ( count( $args ) ) {
public function __call( $fn, array $args= NULL ) {
if ( count($args) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
if ( array_key_exists($fn, $this) )
return $this->{$fn};
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Storage->$fn from $file:$line" );
}
if ( array_key_exists( $fn, $this->defaults ) )
return $this->defaults{$fn};
$backTrace = debug_backtrace();
$file = $backTrace[0]['file'];
$line = $backTrace[0]['line'];
Warning("Unknown function call Storage->$fn from $file:$line");
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning("Unknown function call Storage->$fn from $file:$line");
}
public static function find_one( $parameters = null, $options = null ) {
global $storage_cache;
if (
@ -74,7 +91,8 @@ class Storage {
isset($storage_cache[$parameters['Id']]) ) {
return $storage_cache[$parameters['Id']];
}
$results = Storage::find_all( $parameters, $options );
$results = Storage::find($parameters, $options);
if ( count($results) > 1 ) {
Error("Storage Returned more than 1");
return $results[0];
@ -84,8 +102,7 @@ class Storage {
return null;
}
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
public static function find( $parameters = null, $options = null ) {
$sql = 'SELECT * FROM Storage ';
$values = array();
@ -95,7 +112,7 @@ class Storage {
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
} else if ( is_array($value) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')';
$values += $value;
@ -106,32 +123,47 @@ class Storage {
}
}
$sql .= implode(' AND ', $fields);
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
} # end if parameters
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
} # end if options
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $option['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line");
return array();
}
} # end if limit
} # end if options
$storage_areas = array();
$result = dbQuery($sql, $values);
if ( $result ) {
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
$results = $result->fetchALL();
foreach ( $results as $row ) {
$storage_areas[] = new Storage($row);
}
}
return $filters;
}
return $storage_areas;
} # end find()
public function disk_usage_percent() {
$path = $this->Path();
if ( ! $path ) {
Warning("Storage::disk_usage_percent: path is empty");
Warning('Storage::disk_usage_percent: path is empty');
return 0;
} else if ( ! file_exists( $path ) ) {
} else if ( ! file_exists($path) ) {
Warning("Storage::disk_usage_percent: path $path does not exist");
return 0;
}
$total = $this->disk_total_space();
if ( ! $total ) {
Error("disk_total_space returned false for " . $path );
Error('disk_total_space returned false for ' . $path );
return 0;
}
$used = $this->disk_used_space();
@ -139,6 +171,7 @@ class Storage {
//Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )");
return $usage;
}
public function disk_total_space() {
if ( !array_key_exists('disk_total_space', $this) ) {
$path = $this->Path();
@ -175,8 +208,8 @@ class Storage {
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) {
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) );
foreach ( Event::find_all( array('StorageId'=>$this->Id(), 'DiskSpace'=>null) ) as $Event ) {
$Event->Storage( $this ); // Prevent further db hit
foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) {
$Event->Storage($this); // Prevent further db hit
$used += $Event->DiskSpace();
}
$this->{'DiskSpace'} = $used;

View File

@ -29,16 +29,16 @@ function do_request($method, $url, $data=array(), $optional_headers = null) {
'method' => $method,
'content' => $data
));
if ($optional_headers !== null) {
if ( $optional_headers !== null ) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
if ( !$fp ) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
if ( $response === false ) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
@ -49,16 +49,16 @@ function do_post_request($url, $data, $optional_headers = null) {
'method' => 'POST',
'content' => $data
));
if ($optional_headers !== null) {
if ( $optional_headers !== null ) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
if ( !$fp ) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
if ( $response === false ) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
@ -106,17 +106,17 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're
$responseData = json_decode($res,true);
// PP - credit: https://github.com/google/recaptcha/blob/master/src/ReCaptcha/Response.php
// if recaptcha resulted in error, we might have to deny login
if (isset($responseData['success']) && $responseData['success'] == false) {
if ( isset($responseData['success']) && $responseData['success'] == false ) {
// PP - before we deny auth, let's make sure the error was not 'invalid secret'
// because that means the user did not configure the secret key correctly
// in this case, we prefer to let him login in and display a message to correct
// the key. Unfortunately, there is no way to check for invalid site key in code
// 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 (!in_array('invalid-input-secret',$responseData['error-codes'])) {
if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
if ( !in_array('invalid-input-secret',$responseData['error-codes']) ) {
Error('reCaptcha authentication failed');
userLogout();
$view='login';
$view = 'login';
$refreshParent = true;
return;
} else {
@ -140,19 +140,19 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're
$view = 'none';
} else if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) {
$_COOKIE['zmBandwidth'] = validStr($_REQUEST['newBandwidth']);
setcookie( 'zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10 );
setcookie('zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10);
$refreshParent = true;
}
// Event scope actions, view permissions only required
if ( canView('Events') ) {
if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( $action == 'addterm' ) {
$_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
$_REQUEST['filter'] = addFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
} elseif ( $action == 'delterm' ) {
$_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
} else if ( canEdit( 'Events' ) ) {
$_REQUEST['filter'] = delFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
} else if ( canEdit('Events') ) {
if ( $action == 'delete' ) {
if ( ! empty($_REQUEST['Id']) ) {
dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id']));
@ -210,11 +210,13 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
} else if ( $action == 'eventdetail' ) {
if ( !empty($_REQUEST['eid']) ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid']) );
} else {
$dbConn->beginTransaction();
foreach( getAffectedIds('markEid') as $markEid ) {
dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) );
dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid) );
}
$dbConn->commit();
}
@ -226,7 +228,7 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $_REQUEST['eid']));
} else {
$dbConn->beginTransaction();
foreach( getAffectedIds( 'markEid' ) as $markEid ) {
foreach( getAffectedIds('markEid') as $markEid ) {
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $markEid));
}
$dbConn->commit();
@ -234,8 +236,8 @@ if ( canView('Events') ) {
}
} elseif ( $action == 'delete' ) {
$dbConn->beginTransaction();
foreach( getAffectedIds( 'markEid' ) as $markEid ) {
deleteEvent( $markEid );
foreach( getAffectedIds('markEid') as $markEid ) {
deleteEvent($markEid);
}
$dbConn->commit();
$refreshParent = true;
@ -245,45 +247,47 @@ if ( canView('Events') ) {
} // end canView(Events)
// Monitor control actions, require a monitor id and control view permissions for that monitor
if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
require_once( 'control_functions.php' );
require_once( 'Monitor.php' );
if ( !empty($_REQUEST['mid']) && canView('Control', $_REQUEST['mid']) ) {
require_once('control_functions.php');
require_once('Monitor.php');
$mid = validInt($_REQUEST['mid']);
if ( $action == 'control' ) {
$monitor = new Monitor( $mid );
$monitor = new Monitor($mid);
$ctrlCommand = buildControlCommand( $monitor );
sendControlCommand( $monitor->Id(), $ctrlCommand );
} elseif ( $action == 'settings' ) {
$ctrlCommand = buildControlCommand($monitor);
sendControlCommand($monitor->Id(), $ctrlCommand);
} else if ( $action == 'settings' ) {
$args = ' -m ' . escapeshellarg($mid);
$args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
$args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
$args .= ' -H' . escapeshellarg($_REQUEST['newHue']);
$args .= ' -O' . escapeshellarg($_REQUEST['newColour']);
$zmuCommand = getZmuCommand( $args );
$zmuCommand = getZmuCommand($args);
$zmuOutput = exec( $zmuCommand );
list( $brightness, $contrast, $hue, $colour ) = explode( ' ', $zmuOutput );
dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid));
$zmuOutput = exec($zmuCommand);
list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput);
dbQuery(
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
array($brightness, $contrast, $hue, $colour, $mid));
}
}
// Control capability actions, require control edit permissions
if ( canEdit('Control') ) {
if ( $action == 'controlcap' ) {
require_once( 'Control.php' );
require_once('Control.php');
$Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
$Control->save( $_REQUEST['newControl'] );
$Control->save($_REQUEST['newControl']);
$refreshParent = true;
$view = 'none';
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markCids']) ) {
foreach( $_REQUEST['markCids'] as $markCid ) {
dbQuery( 'delete from Controls where Id = ?', array($markCid) );
dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) );
dbQuery('DELETE FROM Controls WHERE Id = ?', array($markCid));
dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($markCid));
$refreshParent = true;
}
}
@ -293,59 +297,59 @@ if ( canEdit('Control') ) {
if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
if ( $action == 'save' ) {
foreach ( $_REQUEST['mids'] as $mid ) {
$mid = ValidInt( $mid );
if ( ! canEdit('Monitors', $mid ) ) {
$mid = ValidInt($mid);
if ( ! canEdit('Monitors', $mid) ) {
Warning("Cannot edit monitor $mid");
continue;
}
$Monitor = new Monitor( $mid );
$Monitor = new Monitor($mid);
if ( $Monitor->Type() != 'WebSite' ) {
$Monitor->zmaControl('stop');
$Monitor->zmcControl('stop');
}
$Monitor->save( $_REQUEST['newMonitor'] );
if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->save($_REQUEST['newMonitor']);
if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
$Monitor->zmcControl('start');
if ( $Monitor->Enabled() ) {
$Monitor->zmaControl('start');
}
}
} // end foreach mid
$refreshParent = true;
} // end if action == save
} // end if object is Monitor
// Monitor edit actions, require a monitor id and edit permissions for that monitor
if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) {
$mid = validInt($_REQUEST['mid']);
if ( $action == 'function' ) {
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
$newFunction = validStr($_REQUEST['newFunction']);
# Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
$newEnabled = ( !isset( $_REQUEST['newEnabled'] ) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
$newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
$oldFunction = $monitor['Function'];
$oldEnabled = $monitor['Enabled'];
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) );
dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?',
array($newFunction, $newEnabled, $mid));
$monitor['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled;
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, $restart?'restart':'' );
zmaControl( $monitor, 'start' );
zmaControl($monitor, 'stop');
zmcControl($monitor, $restart?'restart':'');
zmaControl($monitor, 'start');
}
$refreshParent = true;
}
} elseif ( $action == 'zone' && isset( $_REQUEST['zid'] ) ) {
} else if ( $action == 'zone' && isset($_REQUEST['zid']) ) {
$zid = validInt($_REQUEST['zid']);
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
if ( !empty($zid) ) {
$zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array( $mid, $zid ) );
$zone = dbFetchOne('SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array($mid, $zid));
} else {
$zone = array();
}
@ -365,74 +369,74 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
unset( $_REQUEST['newZone']['Points'] );
$types = array();
$changes = getFormChanges( $zone, $_REQUEST['newZone'], $types );
$changes = getFormChanges($zone, $_REQUEST['newZone'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( $zid > 0 ) {
dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) );
dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', array($mid, $zid));
} else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid));
}
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl( $monitor, 'stop' );
zmcControl( $monitor, 'restart' );
zmaControl( $monitor, 'start' );
zmaControl($monitor, 'stop');
zmcControl($monitor, 'restart');
zmaControl($monitor, 'start');
} else {
zmaControl( $mid, 'restart' );
zmaControl($monitor, 'restart');
}
}
if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) {
require_once( 'control_functions.php' );
sendControlCommand( $mid, 'quit' );
if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor['Controllable'] ) {
require_once('control_functions.php');
sendControlCommand($mid, 'quit');
}
$refreshParent = true;
}
$view = 'none';
} elseif ( $action == 'plugin' && isset($_REQUEST['pl'])) {
$sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?';
$pconfs=dbFetchAll( $sql, NULL, array( $mid, $_REQUEST['zid'], $_REQUEST['pl'] ) );
$changes=0;
foreach( $pconfs as $pconf ) {
$value=$_REQUEST['pluginOpt'][$pconf['Name']];
if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) {
dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array( $value, $pconf['Id'] ) );
} elseif ( $action == 'plugin' && isset($_REQUEST['pl']) ) {
$sql = 'SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?';
$pconfs=dbFetchAll($sql, NULL, array($mid, $_REQUEST['zid'], $_REQUEST['pl']));
$changes = 0;
foreach ( $pconfs as $pconf ) {
$value = $_REQUEST['pluginOpt'][$pconf['Name']];
if ( array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value'] != $value) ) {
dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array($value, $pconf['Id']));
$changes++;
}
}
if($changes>0) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
zmaControl( $mid, 'restart' );
if ( $changes > 0 ) {
if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
zmaControl($mid, 'restart');
}
$refreshParent = true;
}
$view = 'none';
} elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) {
} elseif ( ($action == 'sequence') && isset($_REQUEST['smid']) ) {
$smid = validInt($_REQUEST['smid']);
$monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) );
$smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid));
$smonitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($smid));
dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) );
dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) );
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($smonitor['Sequence'], $monitor['Id']));
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($monitor['Sequence'], $smonitor['Id']));
$refreshParent = true;
fixSequences();
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markZids']) ) {
$deletedZid = 0;
foreach( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) );
dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) );
foreach ( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne('SELECT * FROM Zones WHERE Id=?', NULL, array($markZid));
dbQuery('DELETE FROM Zones WHERE MonitorId=? AND Id=?', array($mid, $markZid));
$deletedZid = 1;
}
if ( $deletedZid ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
if ( $zone['Type'] == 'Privacy' ) {
zmaControl( $mid, 'stop' );
zmcControl( $mid, 'restart' );
zmaControl( $mid, 'start' );
zmaControl($mid, 'stop');
zmcControl($mid, 'restart');
zmaControl($mid, 'start');
} else {
zmaControl( $mid, 'restart' );
zmaControl($mid, 'restart');
}
} // end if daemonCheck()
$refreshParent = true;
@ -442,15 +446,15 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} // end if $mid and canEdit($mid)
// Monitor edit actions, monitor id derived, require edit permissions for that monitor
if ( canEdit( 'Monitors' ) ) {
if ( canEdit('Monitors') ) {
if ( $action == 'monitor' ) {
$mid = 0;
if ( !empty($_REQUEST['mid']) ) {
$mid = validInt($_REQUEST['mid']);
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
$monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid));
if ( ZM_OPT_X10 ) {
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) );
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid));
if ( !$x10Monitor )
$x10Monitor = array();
}
@ -476,18 +480,19 @@ if ( canEdit( 'Monitors' ) ) {
);
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] );
$_REQUEST['newMonitor']['ServerId'] = dbFetchOne(
'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId'] );
if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
Logger::Debug("Auto selecting server to " . ZM_SERVER_ID);
Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID);
}
}
$columns = getTableColumns('Monitors');
$changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( $mid ) {
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
@ -495,19 +500,19 @@ if ( canEdit( 'Monitors' ) ) {
zmaControl($monitor, 'stop');
zmcControl($monitor, 'stop');
}
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
dbQuery('UPDATE Monitors SET '.implode(', ', $changes).' WHERE Id=?', array($mid));
// Groups will be added below
if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
$OldStorage = new Storage( $monitor['StorageId'] );
$saferOldName = basename( $monitor['Name'] );
if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) )
unlink( $OldStorage->Path().'/'.$saferOldName );
$OldStorage = new Storage($monitor['StorageId']);
$saferOldName = basename($monitor['Name']);
if ( file_exists($OldStorage->Path().'/'.$saferOldName) )
unlink($OldStorage->Path().'/'.$saferOldName);
$NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
if ( ! file_exists( $NewStorage->Path().'/'.$mid ) )
mkdir( $NewStorage->Path().'/'.$mid, 0755 );
$saferNewName = basename( $_REQUEST['newMonitor']['Name'] );
symlink( $mid, $NewStorage->Path().'/'.$saferNewName );
$NewStorage = new Storage($_REQUEST['newMonitor']['StorageId']);
if ( ! file_exists($NewStorage->Path().'/'.$mid) )
mkdir($NewStorage->Path().'/'.$mid, 0755);
$saferNewName = basename($_REQUEST['newMonitor']['Name']);
symlink($mid, $NewStorage->Path().'/'.$saferNewName);
}
if ( isset($changes['Width']) || isset($changes['Height']) ) {
$newW = $_REQUEST['newMonitor']['Width'];
@ -517,15 +522,15 @@ if ( canEdit( 'Monitors' ) ) {
$oldH = $monitor['Height'];
$oldA = $oldW * $oldH;
$zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid) );
$zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid));
foreach ( $zones as $zone ) {
$newZone = $zone;
$points = coordsToPoints( $zone['Coords'] );
$points = coordsToPoints($zone['Coords']);
for ( $i = 0; $i < count($points); $i++ ) {
$points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1));
$points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1));
}
$newZone['Coords'] = pointsToCoords( $points );
$newZone['Coords'] = pointsToCoords($points);
$newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA));
$newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA));
$newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA));
@ -534,41 +539,43 @@ if ( canEdit( 'Monitors' ) ) {
$newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA));
$newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA));
$changes = getFormChanges( $zone, $newZone, $types );
$changes = getFormChanges($zone, $newZone, $types);
if ( count( $changes ) ) {
dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) );
if ( count($changes) ) {
dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
array($mid, $zone['Id']));
}
}
}
} // end foreach zone
} // end if width and height
$restart = true;
} else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors
} else if ( ! $user['MonitorIds'] ) {
// Can only create new monitors if we are not restricted to specific monitors
# FIXME This is actually a race condition. Should lock the table.
$maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
$changes[] = 'Sequence = '.($maxSeq+1);
if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) {
if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) {
$mid = dbInsertId();
$zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height'];
dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
//$view = 'none';
$Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
mkdir( $Storage->Path().'/'.$mid, 0755 );
$Storage = new Storage($_REQUEST['newMonitor']['StorageId']);
mkdir($Storage->Path().'/'.$mid, 0755);
$saferName = basename($_REQUEST['newMonitor']['Name']);
symlink( $mid, $Storage->Path().'/'.$saferName );
symlink($mid, $Storage->Path().'/'.$saferName);
} else {
Error("Error saving new Monitor.");
Error('Error saving new Monitor.');
return;
}
} else {
Error("Users with Monitors restrictions cannot create new monitors.");
Error('Users with Monitors restrictions cannot create new monitors.');
return;
}
$restart = true;
} else {
Logger::Debug("No action due to no changes to Monitor");
Logger::Debug('No action due to no changes to Monitor');
} # end if count(changes)
if (
@ -589,21 +596,21 @@ if ( canEdit( 'Monitors' ) ) {
} // end if there has been a change of groups
if ( ZM_OPT_X10 ) {
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
$x10Changes = getFormChanges($x10Monitor, $_REQUEST['newX10Monitor']);
if ( count( $x10Changes ) ) {
if ( count($x10Changes) ) {
if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) {
dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) );
dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid));
} elseif ( !$user['MonitorIds'] ) {
if ( !$x10Monitor ) {
dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) );
dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid));
} else {
dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) );
dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid));
}
}
$restart = true;
}
}
} # end if has x10Changes
} # end if ZM_OPT_X10
if ( $restart ) {
@ -616,8 +623,8 @@ if ( canEdit( 'Monitors' ) ) {
}
if ( $new_monitor->Controllable() ) {
require_once( 'control_functions.php' );
sendControlCommand( $mid, 'quit' );
require_once('control_functions.php');
sendControlCommand($mid, 'quit');
}
// really should thump zmwatch and maybe zmtrigger too.
//daemonControl( 'restart', 'zmwatch.pl' );
@ -626,11 +633,11 @@ if ( canEdit( 'Monitors' ) ) {
$view = 'none';
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) {
require_once( 'Monitor.php' );
foreach( $_REQUEST['markMids'] as $markMid ) {
require_once('Monitor.php');
foreach ( $_REQUEST['markMids'] as $markMid ) {
if ( canEdit('Monitors', $markMid) ) {
// This could be faster as a select all
if ( $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid) ) ) {
if ( $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid)) ) {
$Monitor = new Monitor($monitor);
$Monitor->delete();
} // end if monitor found in db
@ -641,15 +648,17 @@ if ( canEdit( 'Monitors' ) ) {
}
// Device view actions
if ( canEdit( 'Devices' ) ) {
if ( canEdit('Devices') ) {
if ( $action == 'device' ) {
if ( !empty($_REQUEST['command']) ) {
setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] );
} elseif ( isset( $_REQUEST['newDevice'] ) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) ) {
dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
dbQuery('UPDATE Devices SET Name=?, KeyString=? WHERE Id=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
} else {
dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) );
dbQuery('INSERT INTO Devices SET Name=?, KeyString=?',
array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString']) );
}
$refreshParent = true;
$view = 'none';
@ -657,7 +666,7 @@ if ( canEdit( 'Devices' ) ) {
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markDids']) ) {
foreach( $_REQUEST['markDids'] as $markDid ) {
dbQuery( 'delete from Devices where Id=?', array($markDid) );
dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid));
$refreshParent = true;
}
}
@ -665,47 +674,59 @@ if ( canEdit( 'Devices' ) ) {
} // end if canedit devices
// Group view actions
if ( canView( 'Groups' ) && $action == 'setgroup' ) {
if ( canView('Groups') && ($action == 'setgroup') ) {
if ( !empty($_REQUEST['gid']) ) {
setcookie( 'zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10 );
setcookie('zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10);
} else {
setcookie( 'zmGroup', '', time()-3600*24*2 );
setcookie('zmGroup', '', time()-3600*24*2);
}
$refreshParent = true;
}
// Group edit actions
# Should probably verify that each monitor id is a valid monitor, that we have access to. However at the moment, you have to have System permissions to do this
if ( canEdit( 'Groups' ) ) {
# Should probably verify that each monitor id is a valid monitor, that we have access to.
# However at the moment, you have to have System permissions to do this
if ( canEdit('Groups') ) {
if ( $action == 'group' ) {
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$group_id = null;
if ( !empty($_POST['gid']) ) {
$group_id = $_POST['gid'];
dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) );
dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id) );
dbQuery(
'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
$group_id,
)
);
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id));
} else {
dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) );
$group_id=dbInsertId();
dbQuery(
'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
)
);
$group_id = dbInsertId();
}
if ( $group_id ) {
foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) );
dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid));
}
}
$view = 'none';
$refreshParent = true;
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['gid']) ) {
if ( is_array( $_REQUEST['gid'] ) ) {
foreach( $_REQUEST['gid'] as $gid ) {
$Group = new Group( $gid );
if ( is_array($_REQUEST['gid']) ) {
foreach ( $_REQUEST['gid'] as $gid ) {
$Group = new Group($gid);
$Group->delete();
}
} else {
$Group = new Group( $_REQUEST['gid'] );
$Group = new Group($_REQUEST['gid'] );
$Group->delete();
}
}
@ -714,23 +735,23 @@ if ( canEdit( 'Groups' ) ) {
} // end if can edit groups
// System edit actions
if ( canEdit( 'System' ) ) {
if ( isset( $_REQUEST['object'] ) ) {
if ( canEdit('System') ) {
if ( isset($_REQUEST['object']) ) {
if ( $_REQUEST['object'] == 'MontageLayout' ) {
require_once('MontageLayout.php');
if ( $action == 'Save' ) {
$Layout = null;
if ( $_REQUEST['Name'] != '' ) {
$Layout = new MontageLayout();
$Layout->Name( $_REQUEST['Name'] );
$Layout->Name($_REQUEST['Name']);
} else {
$Layout = new MontageLayout( $_REQUEST['zmMontageLayout'] );
$Layout = new MontageLayout($_REQUEST['zmMontageLayout']);
}
$Layout->Positions( $_REQUEST['Positions'] );
$Layout->Positions($_REQUEST['Positions']);
$Layout->save();
session_start();
$_SESSION['zmMontageLayout'] = $Layout->Id();
setcookie('zmMontageLayout', $Layout->Id(), 1 );
setcookie('zmMontageLayout', $Layout->Id(), 1);
session_write_close();
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview';
} // end if save
@ -738,19 +759,24 @@ if ( canEdit( 'System' ) ) {
} else if ( $_REQUEST['object'] == 'server' ) {
if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) )
$dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) );
else
if ( !empty($_REQUEST['id']) ) {
$dbServer = dbFetchOne(
'SELECT * FROM Servers WHERE Id=?',
NULL,
array($_REQUEST['id']) );
} else {
$dbServer = array();
}
$types = array();
$changes = getFormChanges( $dbServer, $_REQUEST['newServer'], $types );
$changes = getFormChanges($dbServer, $_REQUEST['newServer'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['id']) ) {
dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
dbQuery('UPDATE Servers SET '.implode(', ', $changes).' WHERE Id = ?',
array($_REQUEST['id']) );
} else {
dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Servers SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -758,27 +784,27 @@ if ( canEdit( 'System' ) ) {
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['markIds']) ) {
foreach( $_REQUEST['markIds'] as $Id )
dbQuery( 'DELETE FROM Servers WHERE Id=?', array($Id) );
dbQuery('DELETE FROM Servers WHERE Id=?', array($Id));
}
$refreshParent = true;
} else {
Error( "Unknown action $action in saving Server" );
Error("Unknown action $action in saving Server");
}
} else if ( $_REQUEST['object'] == 'storage' ) {
if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) )
$dbStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']) );
$dbStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']));
else
$dbStorage = array();
$types = array();
$changes = getFormChanges( $dbStorage, $_REQUEST['newStorage'], $types );
$changes = getFormChanges($dbStorage, $_REQUEST['newStorage'], $types);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['id']) ) {
dbQuery( 'UPDATE Storage SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
dbQuery('UPDATE Storage SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['id']));
} else {
dbQuery( 'INSERT INTO Storage set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Storage set '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -786,11 +812,11 @@ if ( canEdit( 'System' ) ) {
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['markIds']) ) {
foreach( $_REQUEST['markIds'] as $Id )
dbQuery( 'DELETE FROM Storage WHERE Id=?', array($Id) );
dbQuery('DELETE FROM Storage WHERE Id=?', array($Id));
}
$refreshParent = true;
} else {
Error( "Unknown action $action in saving Storage" );
Error("Unknown action $action in saving Storage");
}
} # end if isset($_REQUEST['object'] )
@ -804,7 +830,7 @@ if ( canEdit( 'System' ) ) {
}
case 'ignore' :
{
dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" );
dbQuery("UPDATE Config SET Value = '".ZM_DYN_LAST_VERSION."' WHERE Name = 'ZM_DYN_CURR_VERSION'");
break;
}
case 'hour' :
@ -819,12 +845,12 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
break;
}
case 'never' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_CHECK_FOR_UPDATES'");
break;
}
}
@ -852,30 +878,51 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60;
}
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" );
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
break;
}
case 'never' :
case 'already' :
{
dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" );
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
break;
}
} // end switch option
}
if ( ($action == 'privacy') && isset($_REQUEST['option']) ) {
switch( $_REQUEST['option'] ) {
case 'decline' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
case 'accept' :
{
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'");
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_TELEMETRY_DATA'");
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
break;
}
default: # Enable the privacy statement if we somehow submit something other than accept or decline
dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_SHOW_PRIVACY'");
} // end switch option
return;
}
if ( $action == 'options' && isset($_REQUEST['tab']) ) {
$configCat = $configCats[$_REQUEST['tab']];
$changed = false;
foreach ( $configCat as $name=>$value ) {
unset( $newValue );
unset($newValue);
if ( $value['Type'] == 'boolean' && empty($_REQUEST['newConfig'][$name]) ) {
$newValue = 0;
} else if ( isset($_REQUEST['newConfig'][$name]) ) {
$newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) );
$newValue = preg_replace("/\r\n/", "\n", stripslashes($_REQUEST['newConfig'][$name]));
}
if ( isset($newValue) && ($newValue != $value['Value']) ) {
dbQuery( 'UPDATE Config SET Value=? WHERE Name=?', array( $newValue, $name ) );
dbQuery('UPDATE Config SET Value=? WHERE Name=?', array($newValue, $name));
$changed = true;
}
}
@ -901,29 +948,30 @@ if ( canEdit( 'System' ) ) {
}
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab'];
}
loadConfig( false );
loadConfig(false);
return;
} elseif ( $action == 'user' ) {
if ( !empty($_REQUEST['uid']) )
$dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) );
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
else
$dbUser = array();
$types = array();
$changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( $_REQUEST['newUser']['Password'] )
$changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
else
unset( $changes['Password'] );
unset($changes['Password']);
if ( count( $changes ) ) {
if ( count($changes) ) {
if ( !empty($_REQUEST['uid']) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) );
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid']));
# If we are updating the logged in user, then update our session user data.
if ( $user and ( $dbUser['Username'] == $user['Username'] ) )
userLogin( $dbUser['Username'], $dbUser['Password'] );
userLogin($dbUser['Username'], $dbUser['Password']);
} else {
dbQuery( 'insert into Users set '.implode( ', ', $changes ) );
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
}
$refreshParent = true;
}
@ -931,29 +979,28 @@ if ( canEdit( 'System' ) ) {
} elseif ( $action == 'state' ) {
if ( !empty($_REQUEST['runState']) ) {
//if ( $cookies ) session_write_close();
packageControl( $_REQUEST['runState'] );
packageControl($_REQUEST['runState']);
$refreshParent = true;
}
} elseif ( $action == 'save' ) {
if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) {
$sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id';
$definitions = array();
foreach( dbFetchAll( $sql ) as $monitor )
{
foreach( dbFetchAll($sql) as $monitor ) {
$definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled'];
}
$definition = join( ',', $definitions );
$definition = join(',', $definitions);
if ( $_REQUEST['newState'] )
$_REQUEST['runState'] = $_REQUEST['newState'];
dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) );
dbQuery('REPLACE INTO States SET Name=?, Definition=?', array($_REQUEST['runState'],$definition));
}
} elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['runState']) )
dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) );
dbQuery('DELETE FROM States WHERE Name=?', array($_REQUEST['runState']));
if ( isset($_REQUEST['markUids']) ) {
foreach( $_REQUEST['markUids'] as $markUid )
dbQuery( 'delete from Users where Id = ?', array($markUid) );
dbQuery('DELETE FROM Users WHERE Id = ?', array($markUid));
if ( $markUid == $user['Id'] )
userLogout();
}
@ -962,17 +1009,17 @@ if ( canEdit( 'System' ) ) {
if ( ZM_USER_SELF_EDIT && $action == 'user' ) {
$uid = $user['Id'];
$dbUser = dbFetchOne( 'SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid) );
$dbUser = dbFetchOne('SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid));
$types = array();
$changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( !empty($_REQUEST['newUser']['Password']) )
$changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
else
unset( $changes['Password'] );
if ( count( $changes ) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) );
unset($changes['Password']);
if ( count($changes) ) {
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
$refreshParent = true;
}
$view = 'none';
@ -981,8 +1028,8 @@ if ( canEdit( 'System' ) ) {
if ( $action == 'reset' ) {
session_start();
$_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB );
setcookie( 'zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 );
$_SESSION['zmEventResetTime'] = strftime(STRF_FMT_DATETIME_DB);
setcookie('zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10);
session_write_close();
}

View File

@ -139,9 +139,9 @@ function dbQuery( $sql, $params=NULL ) {
}
if ( defined('ZM_DB_DEBUG') ) {
if ( $params )
Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() );
Logger::Debug("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() );
else
Warning("SQL: $sql: rows:" . $result->rowCount() );
Logger::Debug("SQL: $sql: rows:" . $result->rowCount() );
}
} catch(PDOException $e) {
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') );

View File

@ -36,25 +36,27 @@ function noCacheHeaders() {
}
function CORSHeaders() {
if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) {
if ( isset($_SERVER['HTTP_ORIGIN']) ) {
# The following is left for future reference/use.
$valid = false;
$servers = dbFetchAll( 'SELECT * FROM Servers' );
if ( sizeof($servers) <= 1 ) {
$Servers = Server::find();
if ( sizeof($Servers) <= 1 ) {
# Only need CORSHeaders in the event that there are multiple servers in use.
# ICON: Might not be true. multi-port?
return;
}
foreach( $servers as $row ) {
$Server = new Server( $row );
if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) {
foreach( $Servers as $Server ) {
if ( preg_match('/^(https?:\/\/)?'.preg_quote($Server->Hostname(),'/').'/', $_SERVER['HTTP_ORIGIN']) ) {
$valid = true;
header('Access-Control-Allow-Origin: ' . $Server->Url() );
Logger::Debug("Setting Access-Controll-Allow-Origin from " . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Headers: x-requested-with,x-request');
break;
}
}
if ( ! $valid ) {
Warning( $_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.' );
if ( !$valid ) {
Warning($_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.');
}
}
}
@ -910,7 +912,7 @@ function reScale( $dimension, $dummy ) {
$new_dimension = $dimension;
for ( $i = 1; $i < func_num_args(); $i++ ) {
$scale = func_get_arg( $i );
if ( !empty($scale) && $scale != SCALE_BASE )
if ( !empty($scale) && ($scale != 'auto') && ($scale != SCALE_BASE) )
$new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE);
}
return( $new_dimension );

View File

@ -49,8 +49,14 @@ function loadLanguage( $prefix="" )
return( false );
}
if ( $langFile = loadLanguage() )
if ( $langFile = loadLanguage() ) {
require_once( $langFile );
require_once( 'lang/default.php' );
foreach ($DLANG as $key => $value) {
if ( ! array_key_exists( $key, $SLANG ) )
$SLANG[$key] = $DLANG[$key];
}
}
//

View File

@ -406,7 +406,7 @@ class Logger {
$result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) );
} catch(PDOException $ex) {
$this->databaseLevel = self::NOLOG;
Fatal( "Can't write log entry '$sql': ". $ex->getMessage() );
Error("Can't write log entry '$sql': ". $ex->getMessage());
}
}
// This has to be last as trigger_error can be fatal

View File

@ -18,17 +18,17 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
error_reporting( E_ALL );
error_reporting(E_ALL);
$debug = false;
if ( $debug ) {
// Use these for debugging, though not both at once!
phpinfo( INFO_VARIABLES );
phpinfo(INFO_VARIABLES);
//error_reporting( E_ALL );
}
// Use new style autoglobals where possible
if ( version_compare( phpversion(), '4.1.0', '<') ) {
if ( version_compare(phpversion(), '4.1.0', '<') ) {
$_SESSION = &$HTTP_SESSION_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
}
@ -36,20 +36,20 @@ if ( version_compare( phpversion(), '4.1.0', '<') ) {
// Useful debugging lines for mobile devices
if ( false ) {
ob_start();
phpinfo( INFO_VARIABLES );
$fp = fopen( '/tmp/env.html', 'w' );
fwrite( $fp, ob_get_contents() );
fclose( $fp );
phpinfo(INFO_VARIABLES);
$fp = fopen('/tmp/env.html', 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_end_clean();
}
require_once( 'includes/config.php' );
require_once( 'includes/logger.php' );
require_once( 'includes/Server.php' );
require_once( 'includes/Storage.php' );
require_once( 'includes/Event.php' );
require_once( 'includes/Group.php' );
require_once( 'includes/Monitor.php' );
require_once('includes/config.php');
require_once('includes/logger.php');
require_once('includes/Server.php');
require_once('includes/Storage.php');
require_once('includes/Event.php');
require_once('includes/Group.php');
require_once('includes/Monitor.php');
if (
@ -61,13 +61,13 @@ if (
} else {
$protocol = 'http';
}
define( 'ZM_BASE_PROTOCOL', $protocol );
define('ZM_BASE_PROTOCOL', $protocol);
// Absolute URL's are unnecessary and break compatibility with reverse proxies
// define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] );
// Use relative URL's instead
define( 'ZM_BASE_URL', '' );
define('ZM_BASE_URL', '');
// Check time zone is set
if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) {
@ -85,10 +85,10 @@ if ( isset($_GET['skin']) ) {
$skin = 'classic';
}
$skins = array_map( 'basename', glob('skins/*', GLOB_ONLYDIR ) );
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
if ( ! in_array( $skin, $skins ) ) {
Error( "Invalid skin '$skin' setting to " . $skins[0] );
if ( ! in_array($skin, $skins) ) {
Error("Invalid skin '$skin' setting to " . $skins[0]);
$skin = $skins[0];
}
@ -96,25 +96,25 @@ if ( isset($_GET['css']) ) {
$css = $_GET['css'];
} elseif ( isset($_COOKIE['zmCSS']) ) {
$css = $_COOKIE['zmCSS'];
} elseif (defined('ZM_CSS_DEFAULT')) {
} elseif ( defined('ZM_CSS_DEFAULT') ) {
$css = ZM_CSS_DEFAULT;
} else {
$css = 'classic';
}
$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) );
if ( ! in_array( $css, $css_skins ) ) {
Error( "Invalid skin css '$css' setting to " . $css_skins[0] );
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR));
if ( !in_array($css, $css_skins) ) {
Error("Invalid skin css '$css' setting to " . $css_skins[0]);
$css = $css_skins[0];
}
define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) );
define( 'ZM_SKIN_PATH', "skins/$skin" );
define( 'ZM_SKIN_NAME', $skin );
define('ZM_BASE_PATH', dirname($_SERVER['REQUEST_URI']));
define('ZM_SKIN_PATH', "skins/$skin");
define('ZM_SKIN_NAME', $skin);
$skinBase = array(); // To allow for inheritance of skins
if ( !file_exists( ZM_SKIN_PATH ) )
Fatal( "Invalid skin '$skin'" );
if ( !file_exists(ZM_SKIN_PATH) )
Fatal("Invalid skin '$skin'");
$skinBase[] = $skin;
$currentCookieParams = session_get_cookie_params();
@ -127,25 +127,25 @@ session_set_cookie_params(
true
);
ini_set( 'session.name', 'ZMSESSID' );
ini_set('session.name', 'ZMSESSID');
session_start();
if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) {
$_SESSION['skin'] = $skin;
setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 );
setcookie('zmSkin', $skin, time()+3600*24*30*12*10);
}
if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) {
$_SESSION['css'] = $css;
setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 );
setcookie('zmCSS', $css, time()+3600*24*30*12*10);
}
if ( ZM_OPT_USE_AUTH ) {
if ( isset( $_SESSION['user'] ) ) {
if ( isset($_SESSION['user']) ) {
$user = $_SESSION['user'];
} else {
unset( $user );
unset($user);
}
} else {
$user = $defaultUser;
@ -154,9 +154,9 @@ if ( ZM_OPT_USE_AUTH ) {
# Any file/page that sets session variables must re-open it.
session_write_close();
require_once( 'includes/lang.php' );
require_once( 'includes/functions.php' );
require_once( 'includes/auth.php' );
require_once('includes/lang.php');
require_once('includes/functions.php');
require_once('includes/auth.php');
# Running is global but only do the daemonCheck if it is actually needed
$running = null;
@ -179,20 +179,21 @@ $request = null;
if ( isset($_REQUEST['request']) )
$request = detaintPath($_REQUEST['request']);
foreach ( getSkinIncludes( 'skin.php' ) as $includeFile )
foreach ( getSkinIncludes('skin.php') as $includeFile )
require_once $includeFile;
if ( ZM_OPT_USE_AUTH ) {
if ( ZM_AUTH_HASH_LOGINS ) {
if ( empty($user) && ! empty($_REQUEST['auth']) ) {
if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) {
userLogin( $authUser['Username'], $authUser['Password'], true );
if ( ZM_AUTH_HASH_LOGINS && empty($user) && ! empty($_REQUEST['auth']) ) {
if ( $authUser = getAuthUser($_REQUEST['auth']) ) {
userLogin($authUser['Username'], $authUser['Password'], true);
}
}
else if ( isset($_REQUEST['username']) and isset($_REQUEST['password']) ) {
userLogin($_REQUEST['username'], $_REQUEST['password'], false);
}
if ( ! empty($user) ) {
if ( !empty($user) ) {
// generate it once here, while session is open. Value will be cached in session and return when called later on
generateAuthHash( ZM_AUTH_HASH_IPS );
generateAuthHash(ZM_AUTH_HASH_IPS);
}
}
@ -205,40 +206,52 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL;
isset($action) || $action = NULL;
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) {
Logger::Debug("View: $view Request: $request Action: $action");
if (
ZM_ENABLE_CSRF_MAGIC &&
( $action != 'login' ) &&
( $view != 'view_video' ) &&
( $view != 'image' ) &&
( $request != 'control' ) &&
( $view != 'frames' ) &&
( $view != 'archive' )
) {
require_once( 'includes/csrf/csrf-magic.php' );
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();
}
# Need to include actions because it does auth
require_once( 'includes/actions.php' );
require_once('includes/actions.php');
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
if ( ZM_OPT_USE_AUTH and ! isset($user) ) {
Logger::Debug("Redirecting to login" );
if ( ZM_OPT_USE_AUTH and !isset($user) ) {
Logger::Debug('Redirecting to login');
$view = 'login';
$request = null;
} else if ( ZM_SHOW_PRIVACY && ($action != 'privacy') && ($view !='options') && (!$request) && canEdit('System') ) {
Logger::Debug('Redirecting to privacy');
$view = 'privacy';
$request = null;
}
if ( $redirect ) {
header('Location: '.$redirect);
return;
}
if ( $request ) {
foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) {
if ( !file_exists( $includeFile ) )
Fatal( "Request '$request' does not exist" );
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
if ( !file_exists($includeFile) )
Fatal("Request '$request' does not exist");
require_once $includeFile;
}
return;
} else {
if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) {
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
foreach ( $includeFiles as $includeFile ) {
if ( !file_exists( $includeFile ) )
Fatal( "View '$view' does not exist" );
if ( !file_exists($includeFile) )
Fatal("View '$view' does not exist");
require_once $includeFile;
}
// If the view overrides $view to 'error', and the user is not logged in, then the
@ -246,14 +259,14 @@ if ( $request ) {
// The login view should handle redirecting to the correct location afterward.
if ( $view == 'error' && !isset($user) ) {
$view = 'login';
foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile )
foreach ( getSkinIncludes('views/login.php', true, true) as $includeFile )
require_once $includeFile;
}
}
// If the view is missing or the view still returned error with the user logged in,
// then it is not recoverable.
if ( !$includeFiles || $view == 'error' ) {
foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile )
foreach ( getSkinIncludes('views/error.php', true, true) as $includeFile )
require_once $includeFile;
}
}

View File

@ -82,6 +82,8 @@ $SLANG = array(
'Actual' => 'Actual',
'AddNewControl' => '新增控制',
'AddNewMonitor' => '新增監視',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => '新增使用者',
'AddNewZone' => '新增監視區',
'Alarm' => '警報',
@ -109,22 +111,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Archive Status',
'AttrAvgScore' => 'Average Score',
'AttrCause' => 'Cause',
'AttrDate' => 'Date',
'AttrDateTime' => 'Date/Time',
'AttrDiskBlocks' => 'Disk Blocks',
'AttrDiskPercent' => 'Disk Percent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Duration',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Frames',
'AttrId' => 'Id',
'AttrMaxScore' => 'Max. Score',
'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Monitor Name',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Name',
'AttrNotes' => 'Notes',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'System Load',
'AttrTime' => 'Time',
'AttrTotalScore' => 'Total Score',
'AttrWeekday' => 'Weekday',
'Auto' => '自動',
'AutoStopTimeout' => '時間過自動停止',
'Available' => 'Available', // Added - 2009-03-31
@ -157,9 +169,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer',
'BadSectionLength' => 'Section length must be an integer of 30 or more',
'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more',
'BadWarmupCount' => 'Warmup frames must be an integer of zero or more',
'BadWebColour' => 'Web colour must be a valid web colour string',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Width must be set to a valid value',
'Bandwidth' => '頻寬',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -223,10 +237,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => 'Choose Preset',
'Clear' => 'Clear', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => '關閉',
'Colour' => 'Colour',
'Command' => 'Command',
'Component' => 'Component', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config',
'ConfiguredFor' => '配置為',
'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?',
@ -284,9 +300,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Not yet, remind again in 1 week',
'DonateYes' => 'Yes, I\'d like to donate now',
'Download' => '下載',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
'Duration' => '歷時',
'Edit' => '編輯',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => '啟動警報',
'Enabled' => '啟用',
@ -303,6 +321,7 @@ $SLANG = array(
'Events' => '事件',
'Exclude' => '不包含',
'Execute' => 'Execute',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => '輸出',
'ExportDetails' => '輸出事件細項',
'ExportFailed' => '輸出失敗',
@ -332,8 +351,10 @@ $SLANG = array(
'FilterExecuteEvents' => '自動執行符合指令',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => '自動發出符合訊息',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter Px',
'FilterUnset' => '您必需設定濾鏡的寬度和高度',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => '自動上傳符合項目',
'FilterVideoEvents' => '自動產生符合的影像檔',
'Filters' => '濾鏡',
@ -358,6 +379,7 @@ $SLANG = array(
'Function' => '功能',
'Gain' => 'Gain',
'General' => '一般',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => '輸出影片',
'GeneratingVideo' => '輸出影片中',
'GoToZoneMinder' => 'Go to ZoneMinder.com',
@ -378,6 +400,7 @@ $SLANG = array(
'High' => '高',
'HighBW' => 'High&nbsp;B/W',
'Home' => 'Home',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => '時',
'Hue' => 'Hue',
'Id' => 'Id',
@ -402,6 +425,7 @@ $SLANG = array(
'Line' => 'Line', // Added - 2011-06-16
'LinkedMonitors' => 'Linked Monitors',
'List' => '列出',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => '載入',
'Local' => 'Local',
'Log' => 'Log', // Added - 2011-06-16
@ -488,6 +512,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2009-03-31
'Monitors' => '監視',
'Montage' => '全部顯示',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => '月',
'More' => 'More', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -514,6 +539,7 @@ $SLANG = array(
'Next' => '下一步',
'No' => 'No',
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'There are no frames recorded for this event',
'NoGroup' => 'No Group', // Added - 2009-02-08
'NoSavedFilters' => 'NoSavedFilters',
@ -532,6 +558,8 @@ $SLANG = array(
'OpGt' => 'greater than',
'OpGtEq' => 'greater than or equal to',
'OpIn' => 'in set',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'less than',
'OpLtEq' => 'less than or equal to',
'OpMatches' => 'matches',
@ -541,6 +569,7 @@ $SLANG = array(
'Open' => 'Open',
'OptionHelp' => 'OptionHelp',
'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => '銓垣專用',//進階選項
'OrEnterNewName' => 'or enter new name',
'Order' => '順序',
@ -578,9 +607,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocol',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Rate',
'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => '錄影',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => '參考影像混合 %ge',
'Refresh' => '更新',
'Remote' => 'Remote',
@ -596,6 +629,7 @@ $SLANG = array(
'ReplayAll' => 'All Events',
'ReplayGapless' => 'Gapless Events',
'ReplaySingle' => 'Single Event',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Reset',
'ResetEventCounts' => 'Reset Event Counts',
'Restart' => '重新啟動',
@ -614,6 +648,7 @@ $SLANG = array(
'Save' => '存檔',
'SaveAs' => '儲存為',
'SaveFilter' => 'Save Filter',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Scale',
'Score' => '分數',
'Secs' => 'Secs',
@ -630,6 +665,7 @@ $SLANG = array(
'ShowFilterWindow' => '顯示過濾視窗',
'ShowTimeline' => 'Show Timeline',
'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Size',
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
'Sleep' => 'Sleep',
@ -649,6 +685,10 @@ $SLANG = array(
'State' => 'State',
'Stats' => 'Stats',
'Status' => 'Status',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Step',
'StepBack' => 'Step Back',
'StepForward' => 'Step Forward',
@ -659,6 +699,8 @@ $SLANG = array(
'Stills' => '靜止',
'Stop' => '停止',
'Stopped' => '已停止',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => '串流',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Submit' => 'Submit',
@ -678,6 +720,7 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => '時間格式',
'TimestampLabelFormat' => '時間標示格式',
'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30
'TimestampLabelX' => '時間標示 X',
'TimestampLabelY' => '時間標示 Y',
'Today' => 'Today',
@ -724,6 +767,7 @@ $SLANG = array(
'VideoGenParms' => '輸出影片參數',
'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2009-02-08
'VideoSize' => '影片尺寸',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => '檢視',
'ViewAll' => '全部檢視',
'ViewEvent' => 'View Event', // Added - 2009-02-08
@ -733,6 +777,7 @@ $SLANG = array(
'Watch' => 'Watch',
'Web' => 'Web',
'WebColour' => 'Web Colour', // Added - 2009-02-08
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => '週',
'White' => 'White',
'WhiteBalance' => 'White Balance',

View File

@ -78,6 +78,8 @@ $SLANG = array(
'Actual' => '实际',
'AddNewControl' => '新建控制',
'AddNewMonitor' => '新建监视器',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => '新建用户',
'AddNewZone' => '新建区域',
'Alarm' => '报警',
@ -105,22 +107,32 @@ $SLANG = array(
'AttrArchiveStatus' => '存档状态',
'AttrAvgScore' => '平均分数',
'AttrCause' => '原因',
'AttrDate' => '日期',
'AttrDateTime' => '日期/时间',
'AttrDiskBlocks' => '磁碟区块',
'AttrDiskPercent' => '磁碟百分比',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => '过程',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => '帧',
'AttrId' => 'Id',
'AttrMaxScore' => '最大分数',
'AttrMonitorId' => '监视器 Id',
'AttrMonitorName' => '监视器名称',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => '名称',
'AttrNotes' => '备注',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => '系统负载',
'AttrTime' => '时间',
'AttrTotalScore' => '总分数',
'AttrWeekday' => '星期',
'Auto' => '自动',
'AutoStopTimeout' => '超时自动停止',
'Available' => 'Available', // Added - 2009-03-31
@ -153,9 +165,11 @@ $SLANG = array(
'BadRefBlendPerc' => '参考混合百分比必须设为一个正整数',
'BadSectionLength' => '节长度必须设为30的整数倍',
'BadSignalCheckColour' => '信号检查颜色必须设为有效的RGB颜色字符',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer' => '流重放缓冲必须为零或更多整数',
'BadWarmupCount' => '预热帪必须设为零或更多整数',
'BadWebColour' => 'Web颜色必须设为有效Web颜色字符',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => '宽度必须设为有效值',
'Bandwidth' => '带宽',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -219,10 +233,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => '选择预置',
'Clear' => 'Clear', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => '关闭',
'Colour' => '彩色',
'Command' => '命令',
'Component' => 'Component', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => '配置',
'ConfiguredFor' => '配置标的',
'ConfirmDeleteEvents' => '确认希望删除所选事件?',
@ -280,9 +296,11 @@ $SLANG = array(
'DonateRemindWeek' => '现在不1星期内再次提醒我',
'DonateYes' => '好,我现在就捐款',
'Download' => '下载',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
'Duration' => 'Duration',
'Edit' => '编辑',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => '启动报警',
'Enabled' => '已启动',
@ -299,6 +317,7 @@ $SLANG = array(
'Events' => '事件',
'Exclude' => '排除',
'Execute' => '执行',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => '导出',
'ExportDetails' => '导出时间详情',
'ExportFailed' => '导出失败',
@ -328,8 +347,10 @@ $SLANG = array(
'FilterExecuteEvents' => '执行全部匹配项命令',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => '全部匹配项的信息详情',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => '过滤器像素',
'FilterUnset' => '您必须指定过滤器宽度和高度',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => '上传全部匹配项',
'FilterVideoEvents' => '为全部匹配项创建视频',
'Filters' => '过滤器',
@ -354,6 +375,7 @@ $SLANG = array(
'Function' => '功能',
'Gain' => '增益',
'General' => '一般',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => '创建视频',
'GeneratingVideo' => '正在创建视频',
'GoToZoneMinder' => '访问 ZoneMinder.com',
@ -374,6 +396,7 @@ $SLANG = array(
'High' => '高',
'HighBW' => '高&nbsp;B/W',
'Home' => '主页',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => '小时',
'Hue' => '色调',
'Id' => 'Id',
@ -398,6 +421,7 @@ $SLANG = array(
'Line' => 'Line', // Added - 2011-06-16
'LinkedMonitors' => '管理监视器',
'List' => '列表',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => '加载',
'Local' => '本地',
'Log' => 'Log', // Added - 2011-06-16
@ -484,6 +508,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2009-03-31
'Monitors' => '监视器',
'Montage' => '镜头组接',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => '月',
'More' => 'More', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -510,6 +535,7 @@ $SLANG = array(
'Next' => '下一个',
'No' => '不',
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => '该事件没有相关帧的记录',
'NoGroup' => '无组',
'NoSavedFilters' => '没有保存过滤器',
@ -528,6 +554,8 @@ $SLANG = array(
'OpGt' => '大于',
'OpGtEq' => '大于等于',
'OpIn' => '在集',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => '小于',
'OpLtEq' => '小于等于',
'OpMatches' => '匹配',
@ -537,6 +565,7 @@ $SLANG = array(
'Open' => '打开',
'OptionHelp' => '选项帮助',
'OptionRestartWarning' => '这些改动在系统运行时可以不会完全生效.\n 当你设置完毕改动后\n请确认\n您重新启动 ZoneMinder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => '选项',
'OrEnterNewName' => '或输入新名词',
'Order' => '次序',
@ -574,9 +603,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => '协议',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => '速率',
'Real' => '实际',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => '记录',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => '参考影像混合 %ge',
'Refresh' => '刷新',
'Remote' => '远程',
@ -592,6 +625,7 @@ $SLANG = array(
'ReplayAll' => '全部事件',
'ReplayGapless' => '无间隙事件',
'ReplaySingle' => '单一事件',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => '重置',
'ResetEventCounts' => '重置事件数',
'Restart' => '重启动',
@ -610,6 +644,7 @@ $SLANG = array(
'Save' => '保存',
'SaveAs' => '另存为',
'SaveFilter' => '存储过滤器',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => '比例',
'Score' => '分数',
'Secs' => '秒',
@ -626,6 +661,7 @@ $SLANG = array(
'ShowFilterWindow' => '显示过滤器视窗',
'ShowTimeline' => '显示时间轴',
'SignalCheckColour' => '型号检查颜色',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => '大小',
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
'Sleep' => '睡眠',
@ -645,6 +681,10 @@ $SLANG = array(
'State' => '状态',
'Stats' => '统计',
'Status' => '状况',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => '步进',
'StepBack' => '单步后退',
'StepForward' => '单步前进',
@ -655,6 +695,8 @@ $SLANG = array(
'Stills' => '静止',
'Stop' => '停止',
'Stopped' => '已停止',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => '流',
'StreamReplayBuffer' => '流重放影像缓冲',
'Submit' => '发送',
@ -674,6 +716,7 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => '时间戳',
'TimestampLabelFormat' => '时间戳标签格式',
'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30
'TimestampLabelX' => '时间戳标签 X',
'TimestampLabelY' => '时间戳标签 Y',
'Today' => '今天',
@ -720,6 +763,7 @@ $SLANG = array(
'VideoGenParms' => '视频产生参数',
'VideoGenSucceeded' => '视频产生成功!',
'VideoSize' => '视频尺寸',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => '查看',
'ViewAll' => '查看全部',
'ViewEvent' => '查看事件',
@ -729,6 +773,7 @@ $SLANG = array(
'Watch' => '观察',
'Web' => 'Web',
'WebColour' => 'Web颜色',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => '周',
'White' => '白',
'WhiteBalance' => '白平衡',

View File

@ -78,6 +78,8 @@ $SLANG = array(
'Actual' => 'Skutečná',
'AddNewControl' => 'Přidat nové řízení',
'AddNewMonitor' => 'Přidat kameru',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Přidat uživatele',
'AddNewZone' => 'Přidat zónu',
'Alarm' => 'Alarm',
@ -105,22 +107,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Archiv status',
'AttrAvgScore' => 'Prům. skóre',
'AttrCause' => 'Příčina',
'AttrDate' => 'Datum',
'AttrDateTime' => 'Datum/Čas',
'AttrDiskBlocks' => 'Bloky disku',
'AttrDiskPercent' => 'Zaplnění disku',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Průběh',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Snímky',
'AttrId' => 'Id',
'AttrMaxScore' => 'Max. skóre',
'AttrMonitorId' => 'Kamera Id',
'AttrMonitorName' => 'Jméno kamery',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Jméno',
'AttrNotes' => 'Notes',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'System Load',
'AttrTime' => 'Čas',
'AttrTotalScore' => 'Celkové skóre',
'AttrWeekday' => 'Den v týdnu',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Časový limit pro vypršení',
'Available' => 'Available', // Added - 2009-03-31
@ -153,9 +165,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer',
'BadSectionLength' => 'Section length must be an integer of 30 or more',
'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more',
'BadWarmupCount' => 'Warmup frames must be an integer of zero or more',
'BadWebColour' => 'Web colour must be a valid web colour string',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Width must be set to a valid value',
'Bandwidth' => 'Rychlost sítě',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -219,10 +233,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => 'Choose Preset',
'Clear' => 'Clear', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Zavřít',
'Colour' => 'Barva',
'Command' => 'Příkaz',
'Component' => 'Component', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Nastavení',
'ConfiguredFor' => 'Nastaveno pro',
'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?',
@ -280,9 +296,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Nyní ne, připomenout za týden',
'DonateYes' => 'Ano, chcit podpořit ZoneMinder nyní',
'Download' => 'Stáhnout',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
'Duration' => 'Průběh',
'Edit' => 'Editovat',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Povolit alarmy',
'Enabled' => 'Povoleno',
@ -299,6 +317,7 @@ $SLANG = array(
'Events' => 'Záznamy',
'Exclude' => 'Vyjmout',
'Execute' => 'Execute',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exportovat',
'ExportDetails' => 'Exportovat detaily záznamu',
'ExportFailed' => 'Chyba při exportu',
@ -328,8 +347,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Spustit příkaz na všech nalezených',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => 'Podat zprávu o všech nalezených',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtr Px',
'FilterUnset' => 'You must specify a filter width and height',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Uploadovat nalezené',
'FilterVideoEvents' => 'Create video for all matches',
'Filters' => 'Filtry',
@ -354,6 +375,7 @@ $SLANG = array(
'Function' => 'Funkce',
'Gain' => 'Zisk',
'General' => 'General',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Generovat video',
'GeneratingVideo' => 'Generuji video',
'GoToZoneMinder' => 'Jít na ZoneMinder.com',
@ -374,6 +396,7 @@ $SLANG = array(
'High' => 'Rychlá',
'HighBW' => 'Rychlá&nbsp;B/W',
'Home' => 'Domů',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Hodina',
'Hue' => 'Odstín',
'Id' => 'Id',
@ -398,6 +421,7 @@ $SLANG = array(
'Line' => 'Line', // Added - 2011-06-16
'LinkedMonitors' => 'Linked Monitors',
'List' => 'Seznam',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Load',
'Local' => 'Lokální',
'Log' => 'Log', // Added - 2011-06-16
@ -484,6 +508,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2009-03-31
'Monitors' => 'Kamery',
'Montage' => 'Sestřih',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Měsíc',
'More' => 'More', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -510,6 +535,7 @@ $SLANG = array(
'Next' => 'Další',
'No' => 'Ne',
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'Pro tento snímek nejsou žádné záznamy',
'NoGroup' => 'No Group',
'NoSavedFilters' => 'Žádné uložené filtry',
@ -528,6 +554,8 @@ $SLANG = array(
'OpGt' => 'větší',
'OpGtEq' => 'větší nebo rovno',
'OpIn' => 'nin set',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'menší',
'OpLtEq' => 'menší nebo rovno',
'OpMatches' => 'obsahuje',
@ -537,6 +565,7 @@ $SLANG = array(
'Open' => 'Otevřít',
'OptionHelp' => 'MožnostHelp',
'OptionRestartWarning' => 'Tyto změny se neprojeví\ndokud systém běží. Jakmile\ndokončíte provádění změn prosím\nrestartujte ZoneMinder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Možnosti',
'OrEnterNewName' => 'nebo vložte nové jméno',
'Order' => 'Pořadí',
@ -574,9 +603,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocol',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Rychlost',
'Real' => 'Skutečná',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Nahrávat',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Reference Image Blend %ge',
'Refresh' => 'Obnovit',
'Remote' => 'Síťová',
@ -592,6 +625,7 @@ $SLANG = array(
'ReplayAll' => 'All Events',
'ReplayGapless' => 'Gapless Events',
'ReplaySingle' => 'Single Event',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Reset',
'ResetEventCounts' => 'Resetovat počty záznamů',
'Restart' => 'Restartovat',
@ -610,6 +644,7 @@ $SLANG = array(
'Save' => 'Uložit',
'SaveAs' => 'Uložit jako',
'SaveFilter' => 'Uložit filtr',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Velikost',
'Score' => 'Skóre',
'Secs' => 'Délka(s)',
@ -626,6 +661,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Zobrazit filtr',
'ShowTimeline' => 'Zobrazit časovou linii ',
'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Velikost',
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
'Sleep' => 'Spát',
@ -645,6 +681,10 @@ $SLANG = array(
'State' => 'Stav',
'Stats' => 'Statistiky',
'Status' => 'Status',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Krok',
'StepBack' => 'Step Back',
'StepForward' => 'Step Forward',
@ -655,6 +695,8 @@ $SLANG = array(
'Stills' => 'Snímky',
'Stop' => 'Zastavit',
'Stopped' => 'Zastaven',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Submit' => 'Potvrdit',
@ -674,6 +716,7 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => 'Razítko',
'TimestampLabelFormat' => 'Formát časového razítka',
'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30
'TimestampLabelX' => 'Časové razítko X',
'TimestampLabelY' => 'Časové razítko Y',
'Today' => 'Dnes',
@ -720,6 +763,7 @@ $SLANG = array(
'VideoGenParms' => 'Parametry generování videa',
'VideoGenSucceeded' => 'Video vygenerováno úspěšně!',
'VideoSize' => 'Velikost videa',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Zobrazit',
'ViewAll' => 'Zobrazit všechny',
'ViewEvent' => 'Zobrazit záznam',
@ -729,6 +773,7 @@ $SLANG = array(
'Watch' => 'Sledovat',
'Web' => 'Web',
'WebColour' => 'Webová barva',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Týden',
'White' => 'Bílá',
'WhiteBalance' => 'Vyvážení bílé',

View File

@ -80,6 +80,8 @@ $SLANG = array(
'Actual' => 'Original',
'AddNewControl' => 'Neues Steuerelement hinzufügen',
'AddNewMonitor' => 'Neuer Monitor',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Neuer Benutzer',
'AddNewZone' => 'Neue Zone',
'Alarm' => 'Alarm',
@ -107,22 +109,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Archivstatus',
'AttrAvgScore' => 'Mittlere Wertung',
'AttrCause' => 'Grund',
'AttrDate' => 'Datum',
'AttrDateTime' => 'Datum/Zeit',
'AttrDiskBlocks' => 'Disk-Blöcke',
'AttrDiskPercent' => 'Disk-Prozent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Dauer',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Bilder',
'AttrId' => 'ID',
'AttrMaxScore' => 'Maximale Wertung',
'AttrMonitorId' => 'Monitor-ID',
'AttrMonitorName' => 'Monitorname',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Name',
'AttrNotes' => 'Bemerkungen',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'Systemlast',
'AttrTime' => 'Zeit',
'AttrTotalScore' => 'Gesamtwertung',
'AttrWeekday' => 'Wochentag',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung',
'Available' => 'Verfügbar', // Added - 2009-03-31
@ -155,9 +167,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Der Referenz-Blenden-Prozentwert muss ganzzahlig 0 oder größer sein',
'BadSectionLength' => 'Die Bereichslänge muss ganzzahlig 0 oder größer sein',
'BadSignalCheckColour' => 'Die Signalprüffarbe muss auf einen gültigen Farbwert eingestellt sein',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer'=> 'Der Wiedergabestrompuffer muss eine ganze Zahl von null oder mehr betragen',
'BadWarmupCount' => 'Die Anzahl der Vorwärmbilder muss ganzzahlig 0 oder größer sein',
'BadWebColour' => 'Die Webfarbe muss auf einen gültigen Farbwert eingestellt sein',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Die Breite muss auf einen gültigen Wert eingestellt sein',
'Bandwidth' => 'Bandbreite',
'BandwidthHead' => 'Bandbreite', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -221,10 +235,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Log-Auswahl', // Added - 2011-06-17
'ChoosePreset' => 'Voreinstellung auswählen',
'Clear' => 'Leeren', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Schließen',
'Colour' => 'Farbe',
'Command' => 'Kommando',
'Component' => 'Komponente', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Konfig.',
'ConfiguredFor' => 'Konfiguriert für',
'ConfirmDeleteEvents' => 'Sind Sie sicher, dass Sie die ausgewählten Ereignisse löschen wollen?',
@ -282,9 +298,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Noch nicht, erinnere mich in einer Woche noch mal.',
'DonateYes' => 'Ja, ich möchte jetzt spenden.',
'Download' => 'Download',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31
'Duration' => 'Dauer',
'Edit' => 'Bearbeiten',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'E-Mail',
'EnableAlarms' => 'Alarme aktivieren',
'Enabled' => 'Aktiviert',
@ -301,6 +319,7 @@ $SLANG = array(
'Events' => 'Ereignisse',
'Exclude' => 'Ausschluss;',
'Execute' => 'Ausführen',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exportieren',
'ExportDetails' => 'Exportiere Ereignis-Details',
'ExportFailed' => 'Exportieren fehlgeschlagen',
@ -330,8 +349,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Ausführen bei allen Treffern',
'FilterLog' => 'Log filtern', // Added - 2015-04-18
'FilterMessageEvents' => 'Detaillierte Nachricht zu allen Treffern',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter-Pixel',
'FilterUnset' => 'Sie müssen eine Breite und Höhe für das Filter angeben',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Hochladen aller Treffer',
'FilterVideoEvents' => 'Video für alle Treffer erstellen',
'Filters' => 'Filter',
@ -356,6 +377,7 @@ $SLANG = array(
'Function' => 'Funktion',
'Gain' => 'Verstärkung',
'General' => 'Allgemeines',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Erzeuge Video',
'GeneratingVideo' => 'Erzeuge Video...',
'GoToZoneMinder' => 'Besuche ZoneMinder.com',
@ -376,6 +398,7 @@ $SLANG = array(
'High' => 'hohe',
'HighBW' => 'Hohe B/W',
'Home' => 'Home',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Stunde',
'Hue' => 'Farbton',
'Id' => 'ID',
@ -400,6 +423,7 @@ $SLANG = array(
'Line' => 'Zeile', // Added - 2011-06-16
'LinkedMonitors' => 'Verbundene Monitore',
'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Last',
'Local' => 'Lokal',
'Log' => 'Log', // Added - 2011-06-16
@ -486,6 +510,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'Die nachfolgende Liste zeigt erkannte Analog- und Netzwerkkameras, ob sie bereits genutzt werden und ob sie zur Auswahl verfügbar sind.<br/><br/>Wähle den gewünschten Eintrag aus der folgenden Liste.<br/><br/>Bitte Beachten: Nicht alle Kameras können erkannt werden. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.<br/><br/>', // Added - 2009-03-31
'Monitors' => 'Monitore',
'Montage' => 'Montage',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Monat',
'More' => 'Mehr', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -512,6 +537,7 @@ $SLANG = array(
'Next' => 'Nächstes',
'No' => 'Nein',
'NoDetectedCameras' => 'Keine Kameras erkannt', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'Es gibt keine Aufnahmen von diesem Ereignis.',
'NoGroup' => 'Keine Gruppe',
'NoSavedFilters' => 'Keine gespeicherten Filter',
@ -530,6 +556,8 @@ $SLANG = array(
'OpGt' => 'groesser als',
'OpGtEq' => 'groesser oder gleich wie',
'OpIn' => 'in Satz',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'kleiner als',
'OpLtEq' => 'kleiner oder gleich wie',
'OpMatches' => 'zutreffend',
@ -539,6 +567,7 @@ $SLANG = array(
'Open' => 'öffnen',
'OptionHelp' => 'Hilfe',
'OptionRestartWarning' => 'Veränderungen werden erst nach einem Neustart des Programms aktiv.\nFür eine sofortige änderung starten Sie das Programm bitte neu.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Optionen',
'OrEnterNewName' => 'oder neuen Namen eingeben',
'Order' => 'Reihenfolge',
@ -576,9 +605,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'Die folgende Liste zeigt die verfügbaren Streamingprofile der ausgewählten Kamera.<br/><br/>Wähle den gewünschten Eintrag aus der folgenden Liste.<br/><br/>Bitte Beachten: Zoneminder kann keine zusätzlichen Profile konfigurieren. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Fortschritt', // Added - 2015-04-18
'Protocol' => 'Protokoll',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Abspielgeschwindigkeit',
'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Aufnahme',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Referenz-Bildblende',
'Refresh' => 'Aktualisieren',
'Remote' => 'Remote',
@ -594,6 +627,7 @@ $SLANG = array(
'ReplayAll' => 'Alle Ereignisse',
'ReplayGapless' => 'Lückenlose Ereignisse',
'ReplaySingle' => 'Einzelereignis',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Zurücksetzen',
'ResetEventCounts' => 'Lösche Ereignispunktzahl',
'Restart' => 'Neustart',
@ -612,6 +646,7 @@ $SLANG = array(
'Save' => 'Speichern',
'SaveAs' => 'Speichere als',
'SaveFilter' => 'Speichere Filter',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Skalierung',
'Score' => 'Wertung',
'Secs' => 'Sekunden',
@ -628,6 +663,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Zeige Filterfenster',
'ShowTimeline' => 'Zeige Zeitstrahl',
'SignalCheckColour' => 'Farbe des Signalchecks',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Größe',
'SkinDescription' => 'Wähle den standard Skin für diesen Computer.', // Added - 2011-01-30
'Sleep' => 'Schlaf',
@ -647,6 +683,10 @@ $SLANG = array(
'State' => 'Status',
'Stats' => 'Statistik',
'Status' => 'Status',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Stufe',
'StepBack' => 'Einen Schritt rückwärts',
'StepForward' => 'Einen Schritt vorwärts',
@ -657,6 +697,8 @@ $SLANG = array(
'Stills' => 'Standbilder',
'Stop' => 'Stop',
'Stopped' => 'Gestoppt',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream-Wiedergabe-Bildpuffer',
'Submit' => 'Absenden',
@ -676,9 +718,9 @@ $SLANG = array(
'TimelineTip4' => 'Verwenden Sie die Steuerelemente unten, um zu Zoomen oder navigieren Sie vorwärts und rückwärts durch die Zeit.', // Added 2013.08.15.
'Timestamp' => 'Zeitstempel',
'TimestampLabelFormat' => 'Format des Zeitstempels',
'TimestampLabelSize' => 'Schriftgröße',
'TimestampLabelX' => 'Zeitstempel-X',
'TimestampLabelY' => 'Zeitstempel-Y',
'TimestampLabelSize' => 'Schriftgröße',
'Today' => 'Heute',
'Tools' => 'Werkzeuge',
'Total' => 'Insgesamt', // Added - 2011-06-16
@ -723,6 +765,7 @@ $SLANG = array(
'VideoGenParms' => 'Parameter der Videoerzeugung',
'VideoGenSucceeded' => 'Videoerzeugung erfolgreich!',
'VideoSize' => 'Videogröße',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Ansicht',
'ViewAll' => 'Alles ansehen',
'ViewEvent' => 'Zeige Ereignis',
@ -732,6 +775,7 @@ $SLANG = array(
'Watch' => 'Beobachte',
'Web' => 'Web',
'WebColour' => 'Webfarbe',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Woche',
'White' => 'Weiß',
'WhiteBalance' => 'Weiß-Abgleich',

44
web/lang/default.php Normal file
View File

@ -0,0 +1,44 @@
<?php
//
// ZoneMinder DEFAULT English language file, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// 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.
//
// The translate function will return the literal name of the key if the key
// name does not exist in the language file.
// This file provides a means to return a result, other than the key name, if
// it does not exist in the language file.
// Simple String Replacements
$DLANG = array(
'Privacy' => 'Privacy',
'PrivacyAbout' => 'About',
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
'PrivacyContact' => 'Contact',
'PrivacyContactText' => 'Please contact us <a href="https://zoneminder.com/contact/">here</a> for any questions regarding our privacy policy or to have your information removed.<br><br>For support, there are three primary ways to engage with the community:<ul><li>The ZoneMinder <a href="https://forums.zoneminder.com/">user forum</a></li><li>The ZoneMinder <a href="https://zoneminder-chat.herokuapp.com/">Slack channel</a></li><li>The ZoneMinder <a href="https://github.com/ZoneMinder/zoneminder/issues">Github forum</a></li></ul><p>Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.</p>',
'PrivacyCookies' => 'Cookies',
'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.',
'PrivacyTelemetry' => 'Telemetry',
'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.',
'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:<ul><li>A unique identifier (UUID) <li>City based location is gathered by querying <a href="https://ipinfo.io/geo">ipinfo.io</a>. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!<li>Current time<li>Total number of monitors<li>Total number of events<li>System architecture<li>Operating system kernel, distro, and distro version<li>Version of ZoneMinder<li>Total amount of memory<li>Number of cpu cores</ul>',
'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:<ul><li>Id<li>Name<li>Type<li>Function<li>Width<li>Height<li>Colours<li>MaxFPS<li>AlarmMaxFPS</ul>',
'PrivacyConclusionText' => 'We are <u>NOT</u> collecting any image specific data from your cameras. We dont know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.',
);
?>

View File

@ -72,15 +72,6 @@ header( "Content-Type: text/html; charset=utf-8" );
// Simple String Replacements
$SLANG = array(
'SystemLog' => 'System Log',
'DateTime' => 'Dato/Tid',
'Component' => 'Komponent',
'Pid' => 'PID',
'Level' => 'Niveau',
'Message' => 'Meddelelse',
'Line' => 'Linie',
'More' => 'Mere',
'Clear' => 'Slet',
'24BitColour' => '24 bit farve',
'32BitColour' => '32 bit farve',
'8BitGrey' => '8 bit gråskala',
@ -89,6 +80,7 @@ $SLANG = array(
'AddNewControl' => 'Tilføj Ny Kontrol',
'AddNewMonitor' => 'Tilføj Ny Monitor',
'AddNewServer' => 'Tilføj Ny Server',
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Tilføj Ny Bruger',
'AddNewZone' => 'Tilføj Ny Zone',
'Alarm' => 'Alarm',
@ -98,8 +90,8 @@ $SLANG = array(
'AlarmLimits' => 'Alarm Grænser',
'AlarmMaximumFPS' => 'Alarm Maksimum FPS',
'AlarmPx' => 'Alarm Px',
'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %',
'AlarmRGBUnset' => 'Du skal vælge en alarm RGB farve',
'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %',
'Alert' => 'Advarsel',
'All' => 'Alle',
'AnalysisFPS' => 'Analyse FPS',
@ -107,37 +99,45 @@ $SLANG = array(
'Apply' => 'Udfør',
'ApplyingStateChange' => 'Udfører tilstandsændring',
'ArchArchived' => 'Kun arkiverede',
'ArchUnarchived' => 'Kun ikke-arkiverede',
'Archive' => 'Arkivér',
'Archived' => 'Arkiverede',
'ArchUnarchived' => 'Kun ikke-arkiverede',
'Area' => 'Område',
'AreaUnits' => 'Område (px/%)',
'AttrAlarmFrames' => 'Alarm Rammer',
'AttrArchiveStatus' => 'Arkiverings Status',
'AttrAvgScore' => 'Middel Score',
'AttrCause' => 'Årsag',
'AttrDate' => 'Dato',
'AttrDateTime' => 'Dato/Tid',
'AttrDiskBlocks' => 'Disk Blokke',
'AttrDiskPercent' => 'Disk Procent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Varighed',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Rammer',
'AttrId' => 'Id',
'AttrMaxScore' => 'Max. Score',
'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Monitor Navn',
'AttrServer' => 'Server',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Navn',
'AttrNotes' => 'Noter',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'System Belastning',
'AttrTime' => 'Tid',
'AttrTotalScore' => 'Total Score',
'AttrWeekday' => 'Ugedag',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto Stop Timeout',
'Available' => 'Tilgængelig',
'AvgBrScore' => 'Middel<br/>Score',
'Available' => 'Tilgængelig',
'Background' => 'Baggrund',
'BackgroundFilter' => 'Kør filteret i baggrunden',
'BadAlarmFrameCount' => 'Antal alarm rammer skal være et positivt heltal',
@ -145,20 +145,20 @@ $SLANG = array(
'BadAnalysisFPS' => 'Analyse FPS skal være et positivt heltal eller flydende tal',
'BadAnalysisUpdateDelay'=> 'Analyse opdaterings forsinkelse skal være et heltal på 0 eller mere',
'BadChannel' => 'Kanal skal sættes til et heltal på 0 eller mere',
'BadColours' => 'Målfarven skal sættes til en gyldig værdi',
'BadDevice' => 'Enhed skal sættes til en gyldig værdi',
'BadFormat' => 'Format skal sættes til en gyldig værdi',
'BadFPSReportInterval' => 'Antal FPS report interval buffere skal være et heltal på 0 eller mere',
'BadFormat' => 'Format skal sættes til en gyldig værdi',
'BadFrameSkip' => 'Antal Frame skip skal være et heltal på 0 eller mere',
'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere',
'BadHeight' => 'Højde skal sættes til en gyldig værdi',
'BadHost' => 'Host skal vare en gyldig IP adresse eller hostname, inkludér ikke http://',
'BadImageBufferCount' => 'Billed buffer størrelse skal være et heltal på 10 eller mere',
'BadLabelX' => 'Mærkat X co-ordinaten skal sættes til et heltal på 0 eller mere',
'BadLabelY' => 'Mærkat Y co-ordinaten skal sættes til et heltal på 0 eller mere',
'BadMaxFPS' => 'Maximum FPS skal være et positivt heltal eller flydende tal',
'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere',
'BadNameChars' => 'Navne kan kun indeholde alfanumeriske tegn samt mellemrum, bindestreg og understregning',
'BadPalette' => 'Palette skal sættes til en gyldig værdi',
'BadColours' => 'Målfarven skal sættes til en gyldig værdi',
'BadPath' => 'Sti skal sættes til en gyldig værdi',
'BadPort' => 'Port skal sættes til et gyldigt nummer',
'BadPostEventCount' => 'Antal rammer efter hændelsen skal være et heltal på 0 eller mere',
@ -166,39 +166,40 @@ $SLANG = array(
'BadRefBlendPerc' => 'Reference blandings procentdelen skal være et positivt heltal',
'BadSectionLength' => 'Sektionslængden skal være et heltal på 30 eller mere',
'BadSignalCheckColour' => 'Signal check farve skal være en gyldig RGB farve streng',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer' => 'Videostrøm genspilsbufferen skal sættes til et heltal på 0 eller mere',
'BadWarmupCount' => 'Opvarmnings rammer skal være et heltal på 0 eller mere',
'BadWebColour' => 'Web farve skal være en gyldigt web farve streng',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Bredde skal sættes til en gyldig værdi',
'Bandwidth' => 'Båndbredde',
'BandwidthHead' => 'Båndbredde', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
'BlobPx' => 'Blob Px',
'Blobs' => 'Blobs',
'BlobSizes' => 'Blob Størrelser',
'Blobs' => 'Blobs',
'Brightness' => 'Lysstyrke',
'Buffer' => 'Buffer',
'Buffers' => 'Buffere',
'CSSDescription' => 'SKift standard css for denne computer',
'CanAutoFocus' => 'Can Auto Focus',
'CanAutoGain' => 'Can Auto Gain',
'CanAutoIris' => 'Can Auto Iris',
'CanAutoWhite' => 'Can Auto White Bal.',
'CanAutoZoom' => 'Can Auto Zoom',
'Cancel' => 'Fortryd',
'CancelForcedAlarm' => 'Fortryd Tvungen Alarm',
'CanFocusAbs' => 'Can Focus Absolute',
'CanFocus' => 'Can Focus',
'CanFocusAbs' => 'Can Focus Absolute',
'CanFocusCon' => 'Can Focus Continuous',
'CanFocusRel' => 'Can Focus Relative',
'CanGainAbs' => 'Can Gain Absolute',
'CanGain' => 'Can Gain ',
'CanGainAbs' => 'Can Gain Absolute',
'CanGainCon' => 'Can Gain Continuous',
'CanGainRel' => 'Can Gain Relative',
'CanIrisAbs' => 'Can Iris Absolute',
'CanIris' => 'Can Iris',
'CanIrisAbs' => 'Can Iris Absolute',
'CanIrisCon' => 'Can Iris Continuous',
'CanIrisRel' => 'Can Iris Relative',
'CanMoveAbs' => 'Can Move Absolute',
'CanMove' => 'Can Move',
'CanMoveAbs' => 'Can Move Absolute',
'CanMoveCon' => 'Can Move Continuous',
'CanMoveDiag' => 'Can Move Diagonally',
'CanMoveMap' => 'Can Move Mapped',
@ -209,19 +210,21 @@ $SLANG = array(
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',
'CanWake' => 'Can Wake',
'CanWhite' => 'Can White Balance',
'CanWhiteAbs' => 'Can White Bal. Absolute',
'CanWhiteBal' => 'Can White Bal.',
'CanWhite' => 'Can White Balance',
'CanWhiteCon' => 'Can White Bal. Continuous',
'CanWhiteRel' => 'Can White Bal. Relative',
'CanZoomAbs' => 'Can Zoom Absolute',
'CanZoom' => 'Can Zoom',
'CanZoomAbs' => 'Can Zoom Absolute',
'CanZoomCon' => 'Can Zoom Continuous',
'CanZoomRel' => 'Can Zoom Relative',
'Cancel' => 'Fortryd',
'CancelForcedAlarm' => 'Fortryd Tvungen Alarm',
'CaptureHeight' => 'Capture Højde',
'CaptureMethod' => 'Capture Metode',
'CaptureResolution' => 'Capture Opløsning',
'CapturePalette' => 'Capture Palette',
'CaptureResolution' => 'Capture Opløsning',
'CaptureWidth' => 'Capture Bredde',
'Cause' => 'Årsag',
'CheckMethod' => 'Alarm Check Metode',
@ -230,10 +233,13 @@ $SLANG = array(
'ChooseLogFormat' => 'Vælg et lognings format',
'ChooseLogSelection' => 'Vælg et lognings udvælgelse',
'ChoosePreset' => 'Vælg Forudindstilling',
'Clear' => 'Slet',
'CloneMonitor' => 'Klon Monitor',
'Close' => 'Luk',
'Colour' => 'Farve',
'Command' => 'Kommando',
'Component' => 'Komponent',
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Konfigurer',
'ConfiguredFor' => 'Konfigureret for',
'ConfirmDeleteEvents' => 'Er du sikker på, at du vil slette de markerede hændelser?',
@ -244,62 +250,64 @@ $SLANG = array(
'ContactAdmin' => 'Venligst kontakt din administrator for detaljer.',
'Continue' => 'Fortsæt',
'Contrast' => 'Kontrast',
'Control' => 'Control',
'ControlAddress' => 'Control Address',
'ControlCap' => 'Control Capability',
'ControlCaps' => 'Control Capabilities',
'Control' => 'Control',
'ControlDevice' => 'Control Device',
'Controllable' => 'Controllable',
'ControlType' => 'Control Type',
'Controllable' => 'Controllable',
'Current' => 'Nuværende',
'Cycle' => 'Cyklisk',
'CycleWatch' => 'Cyklisk Overvågning',
'DateTime' => 'Dato/Tid',
'Day' => 'Dag',
'Debug' => 'Fejlfind',
'DefaultRate' => 'Standard Rate',
'DefaultScale' => 'Standard Skalering',
'DefaultView' => 'Standard Visning',
'Deinterlacing' => 'Deinterlacing',
'RTSPDescribe' => 'Brug RTSP Response Media URL',
'Delay' => 'Forsilkelse',
'Delete' => 'Slet',
'DeleteAndNext' => 'Slet &amp; Næste',
'DeleteAndPrev' => 'Slet &amp; Forrige',
'Delete' => 'Slet',
'DeleteSavedFilter' => 'Slet gemt filter',
'Description' => 'Beskrivelse',
'DetectedCameras' => 'Fundne Kameraer',
'DetectedProfiles' => 'Fundne Profiler',
'Device' => 'Enheds',
'DeviceChannel' => 'Enheds Kanal',
'DeviceFormat' => 'Enheds Format',
'DeviceNumber' => 'Enheds Number',
'DevicePath' => 'Sti Til Enhed',
'Device' => 'Enheds',
'Devices' => 'Enheder',
'Dimensions' => 'Dimensioner',
'DisableAlarms' => 'Deaktiver Alarmer',
'Disk' => 'Disk',
'Display' => 'Display',
'Displaying' => 'Displaying',
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Venligst Donér',
'DonateAlready' => 'Nej, jeg har allerede doneret',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'Donate' => 'Venligst Donér',
'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag',
'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time',
'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned',
'DonateRemindNever' => 'Nej, jeg ønsker ikke at donere, påmind ikke igen',
'DonateRemindWeek' => 'Ikke endnu, påmind igen on 1 uge',
'DonateYes' => 'Ja, jeg vil gerne donere nu',
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Download' => 'Download',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Dupliket Monitor Navn',
'Duration' => 'Varighed',
'Edit' => 'Ret',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Aktivér Alarmer',
'Enabled' => 'Virksom',
'EnterNewFilterName' => 'Indtast nyt filternavn',
'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer',
'Error' => 'Fejl',
'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer',
'ErrorValidValue' => 'Fejl, venligst check at alle parametre har en gyldig værdi',
'Etc' => 'etc',
'Event' => 'Hændelse',
@ -310,9 +318,9 @@ $SLANG = array(
'Events' => 'Hændelser',
'Exclude' => 'Ekskluder',
'Execute' => 'Udfør',
'ExportDetails' => 'Exporter Hændelses Detaljer',
'Exif' => 'Indlejre EXIF data i billede',
'Export' => 'Exporter',
'ExportDetails' => 'Exporter Hændelses Detaljer',
'ExportFailed' => 'Export Mislykkedes',
'ExportFormat' => 'Export Fil Format',
'ExportFormatTar' => 'Tar',
@ -320,53 +328,55 @@ $SLANG = array(
'ExportFrames' => 'Exporter Ramme Detaljer',
'ExportImageFiles' => 'Exporter billed filer',
'ExportLog' => 'Export Log',
'Exporting' => 'Exporterer',
'ExportMiscFiles' => 'Exporter Andre Filer (hvis tilstede)',
'ExportOptions' => 'Export Indstillinger',
'ExportSucceeded' => 'Export Lykkedes',
'ExportVideoFiles' => 'Exporter Video Filer (hvis tilstede)',
'Exporting' => 'Exporterer',
'FPS' => 'fps',
'FPSReportInterval' => 'FPS Rapport Interval',
'FTP' => 'FTP',
'Far' => 'Fjern',
'FastForward' => 'Hurtigt Frem',
'Feed' => 'Feed',
'Ffmpeg' => 'Ffmpeg',
'File' => 'Fil',
'Filter' => 'Filter',
'FilterArchiveEvents' => 'Arkiver alle matchende',
'FilterDeleteEvents' => 'Slet alle matchende',
'FilterEmailEvents' => 'Email detaljer for alle matchende',
'FilterExecuteEvents' => 'Udfør kommando for alle matchende',
'FilterLog' => 'Filter log',
'FilterMessageEvents' => 'Meddel detaljer for alle matchende',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter Px',
'Filter' => 'Filter',
'Filters' => 'Filtre',
'FilterUnset' => 'Du skal angive filter bredde og højde',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Upload alle match',
'FilterVideoEvents' => 'Opret video for alle match',
'Filters' => 'Filtre',
'First' => 'Første',
'FlippedHori' => 'Spejlet Horizontalt',
'FlippedVert' => 'Spejlet Vertikalt',
'FnNone' => 'None', // Added 2013.08.16.
'FnMonitor' => 'Monitor', // Added 2013.08.16.
'FnModect' => 'Modect', // Added 2013.08.16.
'FnRecord' => 'Record', // Added 2013.08.16.
'FnMocord' => 'Mocord', // Added 2013.08.16.
'FnModect' => 'Modect', // Added 2013.08.16.
'FnMonitor' => 'Monitor', // Added 2013.08.16.
'FnNodect' => 'Nodect', // Added 2013.08.16.
'FnNone' => 'None', // Added 2013.08.16.
'FnRecord' => 'Record', // Added 2013.08.16.
'Focus' => 'Focus',
'ForceAlarm' => 'Force Alarm',
'Format' => 'Format',
'FPS' => 'fps',
'FPSReportInterval' => 'FPS Rapport Interval',
'Frame' => 'Ramme',
'FrameId' => 'Ramme Id',
'FrameRate' => 'Billedhastighed',
'Frames' => 'Rammer',
'FrameSkip' => 'Spring over antal rammer',
'MotionFrameSkip' => 'Spring over antal bevægelsesrammer',
'FTP' => 'FTP',
'Frames' => 'Rammer',
'Func' => 'Funk',
'Function' => 'Funktion',
'Gain' => 'Gain',
'General' => 'Generelt',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Generer Video',
'GeneratingVideo' => 'Genererer Video',
'GoToZoneMinder' => 'Gå til ZoneMinder.com',
@ -384,8 +394,8 @@ $SLANG = array(
'HasTurboTilt' => 'Has Turbo Tilt',
'HasWhiteSpeed' => 'Has White Bal. Speed',
'HasZoomSpeed' => 'Has Zoom Speed',
'HighBW' => 'High&nbsp;B/W',
'High' => 'Høj',
'HighBW' => 'High&nbsp;B/W',
'Home' => 'Hjemme',
'Hostname' => 'Hostname',
'Hour' => 'Time',
@ -393,11 +403,11 @@ $SLANG = array(
'Id' => 'Id',
'Idle' => 'Afventende',
'Ignore' => 'Ignorer',
'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)',
'Image' => 'Billede',
'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)',
'Images' => 'Billeder',
'Include' => 'Inkluder',
'In' => 'I',
'Include' => 'Inkluder',
'Inverted' => 'Inverteret',
'Iris' => 'Blænde',
'KeyString' => 'Nøgle Streng',
@ -405,26 +415,30 @@ $SLANG = array(
'Language' => 'Sprog',
'Last' => 'Sidste',
'Layout' => 'Layout',
'Level' => 'Niveau',
'Libvlc' => 'Libvlc',
'LimitResultsPost' => 'resultater', // This is used at the end of the phrase 'Limit to first N results only'
'LimitResultsPre' => 'Begræns til kun de første', // This is used at the beginning of the phrase 'Limit to first N results only'
'Line' => 'Linie',
'LinkedMonitors' => 'Sammenkædede Monitorer',
'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Belastning',
'Local' => 'Lokal',
'Log' => 'Log',
'Logs' => 'Logs',
'Logging' => 'Logning',
'LoggedInAs' => 'Logget ind som',
'Logging' => 'Logning',
'LoggingIn' => 'Logger ind',
'Login' => 'Logind',
'Logout' => 'Logud',
'LowBW' => 'Lav&nbsp;B/W',
'Logs' => 'Logs',
'Low' => 'Lav',
'LowBW' => 'Lav&nbsp;B/W',
'Main' => 'Hoved',
'Man' => 'Man',
'Manual' => 'Manuel',
'Mark' => 'Markér',
'Max' => 'Max',
'MaxBandwidth' => 'Max Båndbredde',
'MaxBrScore' => 'Max.<br/>Score',
'MaxFocusRange' => 'Max Focus Range',
@ -433,11 +447,9 @@ $SLANG = array(
'MaxGainRange' => 'Max Gain Range',
'MaxGainSpeed' => 'Max Gain Speed',
'MaxGainStep' => 'Max Gain Step',
'MaximumFPS' => 'Maximum FPS',
'MaxIrisRange' => 'Max Iris Range',
'MaxIrisSpeed' => 'Max Iris Speed',
'MaxIrisStep' => 'Max Iris Step',
'Max' => 'Max',
'MaxPanRange' => 'Max Pan Range',
'MaxPanSpeed' => 'Max Pan Speed',
'MaxPanStep' => 'Max Pan Step',
@ -450,8 +462,10 @@ $SLANG = array(
'MaxZoomRange' => 'Max Zoom Range',
'MaxZoomSpeed' => 'Max Zoom Speed',
'MaxZoomStep' => 'Max Zoom Step',
'MediumBW' => 'Medium&nbsp;B/W',
'MaximumFPS' => 'Maximum FPS',
'Medium' => 'Medium',
'MediumBW' => 'Medium&nbsp;B/W',
'Message' => 'Meddelelse',
'MinAlarmAreaLtMax' => 'Minimum alarm område skal være mindre end maksimum',
'MinAlarmAreaUnset' => 'Du skal angive det minimale antal alarm pixels',
'MinBlobAreaLtMax' => 'Minimum blob område skal være mindre end maksimum',
@ -487,22 +501,24 @@ $SLANG = array(
'MinZoomStep' => 'Min Zoom Step',
'Misc' => 'Diverse',
'Mode' => 'Mode',
'MonitorIds' => 'Monitor&nbsp;Ids',
'Monitor' => 'Monitor',
'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.<br/><br/>Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.<br/><br/>',
'MonitorIds' => 'Monitor&nbsp;Ids',
'MonitorPreset' => 'Monitor Forudindstillinger',
'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.<br/><br/>Vælg det ønskede fra listen herunder.<br/><br/>Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.<br/><br/>',
'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.<br/><br/>Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.<br/><br/>',
'MonitorProbe' => 'Monitor Probe',
'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.<br/><br/>Vælg det ønskede fra listen herunder.<br/><br/>Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.<br/><br/>',
'Monitors' => 'Monitorer',
'Montage' => 'Montage',
'MontageReview' => 'Montage Review',
'Month' => 'Måned',
'More' => 'Mere',
'MotionFrameSkip' => 'Spring over antal bevægelsesrammer',
'Move' => 'Bevæg',
'MtgDefault' => 'Standard', // Added 2013.08.15.
'Mtg2widgrd' => '2-bred gitter', // Added 2013.08.15.
'Mtg3widgrd' => '3-bred gitter', // Added 2013.08.15.
'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15.
'Mtg3widgrx' => '3-bred gitter, skaleret, forstørret ved alarm', // Added 2013.08.15.
'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15.
'MtgDefault' => 'Standard', // Added 2013.08.15.
'MustBeGe' => 'Skal være større end eller lig med',
'MustBeLe' => 'Skal være mindre end eller lig med',
'MustConfirmPassword' => 'Du skal bekræfte adgangskoden',
@ -511,53 +527,55 @@ $SLANG = array(
'Name' => 'Navn',
'Near' => 'Nær',
'Network' => 'Netværk',
'New' => 'Ny',
'NewGroup' => 'Ny Gruppe',
'NewLabel' => 'Ny Mærkat',
'New' => 'Ny',
'NewPassword' => 'Ny Adgangskode',
'NewState' => 'Ny Tilstand',
'NewUser' => 'Ny bruger',
'Next' => 'Næste',
'No' => 'Nej',
'NoDetectedCameras' => 'Ingen Detected Cameras',
'NoDetectedProfiles' => 'Ingen Fundne Profiler',
'NoFramesRecorded' => 'Der er ingen billeder optaget for denne hændelse',
'NoGroup' => 'Ingen gruppe',
'NoneAvailable' => 'Ingen tilgængelig',
'None' => 'Ingen',
'No' => 'Nej',
'Normal' => 'Normalt',
'NoSavedFilters' => 'IngenGemteFiltre',
'NoStatisticsRecorded' => 'Der er ingen statistik noteret for denne hændelse/ramme',
'None' => 'Ingen',
'NoneAvailable' => 'Ingen tilgængelig',
'Normal' => 'Normalt',
'Notes' => 'Noter',
'NumPresets' => 'Num Forudinst.',
'Off' => 'Fra',
'On' => 'Til',
'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.<br/>Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.<br/><br/>',
'OnvifProbe' => 'ONVIF',
'OnvifProbeIntro' => 'Listen nedenfor viser fundne ONVIF kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.<br/><br/>Vælg det ønskede fra listen herunder.<br/><br/>Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.<br/><br/>',
'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.<br/>Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.<br/><br/>',
'Open' => 'Åben',
'OpEq' => 'lig med',
'OpGtEq' => 'større end eller lig med',
'OpGt' => 'større end',
'OpGtEq' => 'større end eller lig med',
'OpIn' => 'indeholdt i',
'OpLtEq' => 'mindre end eller lig med',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'mindre end',
'OpLtEq' => 'mindre end eller lig med',
'OpMatches' => 'matcher',
'OpNe' => 'ikke lig med',
'OpNotIn' => 'ikke indeholdt i',
'OpNotMatches' => 'matcher ikke',
'OptionalEncoderParam' => 'Optionelle Encoder Parametre',
'Open' => 'Åben',
'OptionHelp' => 'Indstillinger hjælp',
'OptionRestartWarning' => 'Disse ændringer har muligvis ikke fuld effekt\nmens systemet er kørende. Når du har\nafsluttet dine ændringer, skal du huske at\ngenstarte ZoneMinder.',
'OptionalEncoderParam' => 'Optionelle Encoder Parametre',
'Options' => 'Indstillinger',
'Order' => 'Rækkefølge',
'OrEnterNewName' => 'eller indtast nyt navn',
'Order' => 'Rækkefølge',
'Orientation' => 'Orientering',
'Out' => 'Ud',
'OverwriteExisting' => 'Overskriv Eksisterende',
'Paged' => 'Sidevis',
'PanLeft' => 'Pan Left',
'Pan' => 'Pan',
'PanLeft' => 'Pan Left',
'PanRight' => 'Pan Right',
'PanTilt' => 'Pan/Tilt',
'Parameter' => 'Parameter',
@ -565,14 +583,15 @@ $SLANG = array(
'PasswordsDifferent' => 'Den nye og den bekræftende adgangskode er forskellige',
'Paths' => 'Stier',
'Pause' => 'Pause',
'PhoneBW' => 'Telefon&nbsp;B/W',
'Phone' => 'Telefon',
'PhoneBW' => 'Telefon&nbsp;B/W',
'Pid' => 'PID',
'PixelDiff' => 'Pixel Forskel',
'Pixels' => 'pixels',
'PlayAll' => 'Afspil Alle',
'Play' => 'Afspil',
'Plugins' => 'Plugins',
'PlayAll' => 'Afspil Alle',
'PleaseWait' => 'Vent venligst',
'Plugins' => 'Plugins',
'Point' => 'Point',
'PostEventImageBuffer' => 'Antal Billeder Efter Hændelse',
'PreEventImageBuffer' => 'Antal Billeder Før Hændelse',
@ -585,30 +604,33 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>',
'Progress' => 'Position',
'Protocol' => 'Protokol',
'RTSPDescribe' => 'Brug RTSP Response Media URL',
'RTSPTransport' => 'RTSP Transport Protocol',
'Rate' => 'Rate',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP
'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.',
'Real' => 'Naturtro',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP
'Record' => 'Optag',
'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.',
'RefImageBlendPct' => 'Reference Billede Blandings %',
'Refresh' => 'Genindlæs',
'Remote' => 'Remote',
'RemoteHostName' => 'Remote Host Name',
'RemoteHostPath' => 'Remote Host Path',
'RemoteHostSubPath' => 'Remote Host SubPath',
'RemoteHostPort' => 'Remote Host Port',
'RemoteHostSubPath' => 'Remote Host SubPath',
'RemoteImageColours' => 'Remote Image Colours',
'RemoteMethod' => 'Remote Method',
'RemoteProtocol' => 'Remote Protocol',
'Remote' => 'Remote',
'Rename' => 'Omdøb',
'Replay' => 'Genafspil',
'ReplayAll' => 'Alle Hændelser',
'ReplayGapless' => 'Hændelser uafbrudt',
'Replay' => 'Genafspil',
'ReplaySingle' => 'Enkelt Hændelse',
'ResetEventCounts' => 'Nulstil Hændelses Tæller',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Nulstil',
'Restarting' => 'Genstarter',
'ResetEventCounts' => 'Nulstil Hændelses Tæller',
'Restart' => 'Genstart',
'Restarting' => 'Genstarter',
'RestrictedCameraIds' => 'Restricted Camera Ids',
'RestrictedMonitors' => 'Restricted Monitors',
'ReturnDelay' => 'Return Delay',
@ -616,34 +638,33 @@ $SLANG = array(
'Rewind' => 'Hurtigt Tilbage',
'RotateLeft' => 'Roter til venstrte',
'RotateRight' => 'Roter til højre',
'RTSPTransport' => 'RTSP Transport Protocol',
'RunLocalUpdate' => 'Kør venligst zmupdate.pl for at opdatere',
'RunMode' => 'Driftsmåde',
'Running' => 'Kørende',
'RunState' => 'Drift Tilstand',
'Running' => 'Kørende',
'Save' => 'Gem',
'SaveAs' => 'Gem som',
'SaveFilter' => 'Gem Filter',
'SaveJPEGS' => 'Gem JPEGs',
'Save' => 'Gem',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Skaler',
'Score' => 'Score',
'Secs' => 'Sek.',
'Sectionlength' => 'Sektions længde',
'SelectMonitors' => 'Vælg Monitorer',
'Select' => 'Vælg',
'SelectFormat' => 'Vælg Format',
'SelectLog' => 'Vælg Log',
'SelectMonitors' => 'Vælg Monitorer',
'SelfIntersecting' => 'Polygonens kanter må ikke krydses',
'Set' => 'Sæt',
'SetNewBandwidth' => 'Vælg ny båndbredde',
'SetPreset' => 'Set Preset',
'Set' => 'Sæt',
'Settings' => 'Indstillinger',
'ShowFilterWindow' => 'Vis Filter Vindue',
'ShowTimeline' => 'Vis Tidslinie',
'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Størrelse',
'SkinDescription' => 'SKift standard skin for denne computer',
'CSSDescription' => 'SKift standard css for denne computer',
'Sleep' => 'Sover',
'SortAsc' => 'Voksende',
'SortBy' => 'Sortér efter',
@ -652,46 +673,53 @@ $SLANG = array(
'SourceColours' => 'Kilde Farver',
'SourcePath' => 'Kilde Sti',
'SourceType' => 'Kilde Type',
'Speed' => 'Speed',
'SpeedHigh' => 'High Speed',
'SpeedLow' => 'Low Speed',
'SpeedMedium' => 'Medium Speed',
'Speed' => 'Speed',
'SpeedTurbo' => 'Turbo Speed',
'Start' => 'Start',
'State' => 'Tilstand',
'Stats' => 'Stats',
'Status' => 'Status',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Skridt',
'StepBack' => 'Skridt Tilbage',
'StepForward' => 'Skridt Frem',
'StepLarge' => 'Langt Skridt',
'StepMedium' => 'Medium Skridt',
'StepNone' => 'Ingen Skridt',
'StepSmall' => 'Lille Skridt',
'Step' => 'Skridt',
'Stills' => 'Stilbilleder',
'Stopped' => 'Stoppet',
'Stop' => 'Stop',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Stopped' => 'Stoppet',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Submit' => 'Påtryk',
'System' => 'System',
'SystemLog' => 'System Log',
'TargetColorspace' => 'Target colorspace',
'Tele' => 'Tele',
'Thumbnail' => 'Thumbnail',
'Tilt' => 'Tilt',
'Time' => 'Tidspunkt',
'TimeDelta' => 'Tidsforskel',
'TimeStamp' => 'Tids stempel',
'Timeline' => 'Tidslinie',
'TimelineTip1' => 'Før musen over grafen for at vise snapshot billede og detaljer om hændelsen.', // Added 2013.08.15.
'TimelineTip2' => 'Klip på de farvede områder af grafen, eller billedet, for at se hændelsen.', // Added 2013.08.15.
'TimelineTip3' => 'Klik på baggrunden for at zoome ind på en mindre tidsperiode omkring dit klik.', // Added 2013.08.15.
'TimelineTip4' => 'Brug kontrollerne nedenfor for at zoome ud eller navigere frem eller tilbage i tiden.', // Added 2013.08.15.
'Timestamp' => 'Tidsstempel',
'TimestampLabelFormat' => 'Tidsstempel Mærkat Format',
'TimestampLabelSize' => 'Font Størrelse',
'TimestampLabelX' => 'Tidsstempel Mærkat X',
'TimestampLabelY' => 'Tidsstempel Mærkat Y',
'TimestampLabelSize' => 'Font Størrelse',
'Timestamp' => 'Tidsstempel',
'TimeStamp' => 'Tids stempel',
'Time' => 'Tidspunkt',
'Today' => 'Idag',
'Tools' => 'Værktøjer',
'Total' => 'Total',
@ -706,25 +734,29 @@ $SLANG = array(
'Undefined' => 'Udefineret',
'Units' => 'Enheder',
'Unknown' => 'Ukendt',
'Update' => 'Opdater',
'UpdateAvailable' => 'En opdatering til ZoneMinder er tilgængelig.',
'UpdateNotNecessary' => 'Ingen opdatering er nødvendig.',
'Update' => 'Opdater',
'Upload' => 'Upload',
'Updated' => 'Opdateret',
'UsedPlugins' => 'Anvendte Plugins',
'Upload' => 'Upload',
'UseFilter' => 'Anvend Filter',
'UseFilterExprsPost' => '&nbsp;filter&nbsp;udtryk', // This is used at the end of the phrase 'use N filter expressions'
'UseFilterExprsPre' => 'Anvend&nbsp;', // This is used at the beginning of the phrase 'use N filter expressions'
'UseFilter' => 'Anvend Filter',
'UsedPlugins' => 'Anvendte Plugins',
'User' => 'Bruger',
'Username' => 'Brugernavn',
'Users' => 'Brugere',
'User' => 'Bruger',
'V4L' => 'V4L',
'V4LCapturesPerFrame' => 'Captures Per Frame',
'V4LMultiBuffer' => 'Multi Buffering',
'Value' => 'Værdi',
'Version' => 'Version',
'VersionIgnore' => 'Ignorer denne værdi',
'VersionRemindDay' => 'Påmind igen om 1 dag',
'VersionRemindHour' => 'Påmind igen om 1 time',
'VersionRemindNever' => 'Påmind ikke om nye versioner',
'VersionRemindWeek' => 'Påmind igen om 1 uge',
'Version' => 'Version',
'Video' => 'Video',
'VideoFormat' => 'Video Format',
'VideoGenFailed' => 'Video Generering Fejlede!',
'VideoGenFiles' => 'Existerende Video Filer',
@ -733,47 +765,44 @@ $SLANG = array(
'VideoGenSucceeded' => 'Video Generering Succeeded!',
'VideoSize' => 'Video Størrelse',
'VideoWriter' => 'Video Skriver',
'Video' => 'Video',
'View' => 'Vis',
'ViewAll' => 'Vis Alle',
'ViewEvent' => 'Vis Hændelse',
'ViewPaged' => 'Vis Sidevis',
'View' => 'Vis',
'V4L' => 'V4L',
'V4LCapturesPerFrame' => 'Captures Per Frame',
'V4LMultiBuffer' => 'Multi Buffering',
'Wake' => 'Vågen',
'WarmupFrames' => 'Opvarmningsbilleder',
'Watch' => 'Ur',
'WebColour' => 'Web Farve',
'Web' => 'Web',
'WebColour' => 'Web Farve',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Uge',
'WhiteBalance' => 'Hvidbalance',
'White' => 'Hvid',
'WhiteBalance' => 'Hvidbalance',
'Wide' => 'Bred',
'X' => 'X',
'X10' => 'X10',
'X10ActivationString' => 'X10 Activerings Streng',
'X10InputAlarmString' => 'X10 Input Alarm Streng',
'X10OutputAlarmString' => 'X10 Output Alarm Streng',
'X10' => 'X10',
'X' => 'X',
'Y' => 'Y',
'Yes' => 'Ja',
'YouNoPerms' => 'Du har ikke tilladelse til at tilgå denne ressurse.',
'Y' => 'Y',
'Zone' => 'Zone',
'ZoneAlarmColour' => 'Alarm Farve (Rød/Grøn/Blå)',
'ZoneArea' => 'Zone Område',
'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer',
'ZoneFilterSize' => 'Filter Bredde/Højde (pixels)',
'ZoneMinderLog' => 'ZoneMinder Log',
'ZoneMinMaxAlarmArea' => 'Min/Max Alarmeret Område',
'ZoneMinMaxBlobArea' => 'Min/Max Blob Område',
'ZoneMinMaxBlobs' => 'Min/Max Blobs',
'ZoneMinMaxFiltArea' => 'Min/Max Filtreret Område',
'ZoneMinMaxPixelThres' => 'Min/Max Pixel Grænseværdi (0-255)',
'ZoneMinderLog' => 'ZoneMinder Log',
'ZoneOverloadFrames' => 'Antal Rammer At Ignorere Efter Overload',
'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer',
'Zones' => 'Zoner',
'Zone' => 'Zone',
'Zoom' => 'Zoom',
'ZoomIn' => 'Zoom Ind',
'ZoomOut' => 'Zoom Ud',
'Zoom' => 'Zoom',
);
// Complex replacements with formatting and/or placements, must be passed through sprintf

View File

@ -600,6 +600,18 @@ $SLANG = array(
'Preset' => 'Preset',
'Presets' => 'Presets',
'Prev' => 'Prev',
'Privacy' => 'Privacy',
'PrivacyAbout' => 'About',
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
'PrivacyContact' => 'Contact',
'PrivacyContactText' => 'Please contact us <a href="https://zoneminder.com/contact/">here</a> for any questions regarding our privacy policy or to have your information removed.<br><br>For support, there are three primary ways to engage with the community:<ul><li>The ZoneMinder <a href="https://forums.zoneminder.com/">user forum</a></li><li>The ZoneMinder <a href="https://zoneminder-chat.herokuapp.com/">Slack channel</a></li><li>The ZoneMinder <a href="https://github.com/ZoneMinder/zoneminder/issues">Github forum</a></li></ul><p>Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.</p>',
'PrivacyCookies' => 'Cookies',
'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.',
'PrivacyTelemetry' => 'Telemetry',
'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.',
'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:<ul><li>A unique identifier (UUID) <li>City based location is gathered by querying <a href="https://ipinfo.io/geo">ipinfo.io</a>. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!<li>Current time<li>Total number of monitors<li>Total number of events<li>System architecture<li>Operating system kernel, distro, and distro version<li>Version of ZoneMinder<li>Total amount of memory<li>Number of cpu cores</ul>',
'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:<ul><li>Id<li>Name<li>Type<li>Function<li>Width<li>Height<li>Colours<li>MaxFPS<li>AlarmMaxFPS</ul>',
'PrivacyConclusionText' => 'We are <u>NOT</u> collecting any image specific data from your cameras. We dont know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.',
'Probe' => 'Probe',
'ProfileProbe' => 'Stream Probe',
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>',
@ -638,10 +650,13 @@ $SLANG = array(
'RotateLeft' => 'Rotate Left',
'RotateRight' => 'Rotate Right',
'RTSPTransport' => 'RTSP Transport Protocol',
'RunAudit' => 'Run Audit Process',
'RunLocalUpdate' => 'Please run zmupdate.pl to update',
'RunMode' => 'Run Mode',
'Running' => 'Running',
'RunState' => 'Run State',
'RunStats' => 'Run Stats Process',
'RunTrigger' => 'Run Trigger Process',
'SaveAs' => 'Save as',
'SaveFilter' => 'Save Filter',
'SaveJPEGs' => 'Save JPEGs',
@ -698,6 +713,7 @@ $SLANG = array(
'Stopped' => 'Stopped',
'Stop' => 'Stop',
'StorageArea' => 'Storage Area',
'StorageDoDelete' => 'Do Deletes',
'StorageScheme' => 'Scheme',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Stream' => 'Stream',

View File

@ -29,6 +29,8 @@ $SLANG = array(
'Actual' => 'Actual',
'AddNewControl' => 'Add New Control',
'AddNewMonitor' => 'Agregar Nuevo Monitor',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Agregar Nuevo Usuario',
'AddNewZone' => 'Agregar Nueva Zona',
'Alarm' => 'Alarma',
@ -56,22 +58,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Estado Archivo',
'AttrAvgScore' => 'Puntaje Prom.',
'AttrCause' => 'Cause',
'AttrDate' => 'Fecha',
'AttrDateTime' => 'Fecha/Hora',
'AttrDiskBlocks' => 'Disk Blocks',
'AttrDiskPercent' => 'Disk Percent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Duración',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Cuadros',
'AttrId' => 'Id',
'AttrMaxScore' => 'Puntaje Máximo',
'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Nombre Monitor',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Name',
'AttrNotes' => 'Notes',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'System Load',
'AttrTime' => 'Hora',
'AttrTotalScore' => 'Puntaje Total',
'AttrWeekday' => 'Día Semana',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto Stop Timeout',
'Available' => 'Available', // Added - 2009-03-31
@ -104,9 +116,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer',
'BadSectionLength' => 'Section length must be an integer of 30 or more',
'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more',
'BadWarmupCount' => 'Warmup frames must be an integer of zero or more',
'BadWebColour' => 'Web colour must be a valid web colour string',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Width must be set to a valid value',
'Bandwidth' => 'Velocidad',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -170,10 +184,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => 'Choose Preset',
'Clear' => 'Clear', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Cerrar',
'Colour' => 'Color',
'Command' => 'Command',
'Component' => 'Component', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config.',
'ConfiguredFor' => 'Configurado Para',
'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?',
@ -231,9 +247,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Not yet, remind again in 1 week',
'DonateYes' => 'Yes, I\'d like to donate now',
'Download' => 'Download',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
'Duration' => 'Duración',
'Edit' => 'Editar',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Enable Alarms',
'Enabled' => 'Habilitado',
@ -250,6 +268,7 @@ $SLANG = array(
'Events' => 'Eventos',
'Exclude' => 'Excluir',
'Execute' => 'Execute',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Export',
'ExportDetails' => 'Export Event Details',
'ExportFailed' => 'Export Failed',
@ -279,8 +298,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Ejecutar un comando en las coincidencias',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => 'Mandar un mensaje de los eventos',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtro Px',
'FilterUnset' => 'You must specify a filter width and height',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Subir los eventos que coincidan',
'FilterVideoEvents' => 'Create video for all matches',
'Filters' => 'Filters',
@ -305,6 +326,7 @@ $SLANG = array(
'Function' => 'Función',
'Gain' => 'Gain',
'General' => 'General',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Crear Video',
'GeneratingVideo' => 'Creando Video',
'GoToZoneMinder' => 'Ir a Zoneminder.com',
@ -325,6 +347,7 @@ $SLANG = array(
'High' => 'Alta',
'HighBW' => 'Alta&nbsp;B/W',
'Home' => 'Home',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Hora',
'Hue' => 'Saturación',
'Id' => 'Id',
@ -349,6 +372,7 @@ $SLANG = array(
'Line' => 'Line', // Added - 2011-06-16
'LinkedMonitors' => 'Linked Monitors',
'List' => 'List',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Carga',
'Local' => 'Local',
'Log' => 'Log', // Added - 2011-06-16
@ -435,6 +459,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2009-03-31
'Monitors' => 'Monitores',
'Montage' => 'Cámara Múltiple',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Mes',
'More' => 'More', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -461,6 +486,7 @@ $SLANG = array(
'Next' => 'Siguiente',
'No' => 'No',
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'No hay movimientos grabados para este evento',
'NoGroup' => 'No Group',
'NoSavedFilters' => 'FiltrosNoGuardados',
@ -479,6 +505,8 @@ $SLANG = array(
'OpGt' => 'mayor que',
'OpGtEq' => 'mayor o igual que',
'OpIn' => 'En sistema',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'menor que',
'OpLtEq' => 'menor o igual que',
'OpMatches' => 'Coincide',
@ -488,6 +516,7 @@ $SLANG = array(
'Open' => 'Open',
'OptionHelp' => 'Ayuda',
'OptionRestartWarning' => 'Estos cambios no se guardaran completamente\nmientras el sistema se ejecute. Cuando termine\nde realizar los cambios asegurese de\nreiniciar Zoneminder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Opciones',
'OrEnterNewName' => 'o agregue nombre',
'Order' => 'Order',
@ -525,9 +554,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocol',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Ritmo',
'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Registro',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Reference Image Blend %ge',
'Refresh' => 'Actualizar',
'Remote' => 'Remote',
@ -543,6 +576,7 @@ $SLANG = array(
'ReplayAll' => 'All Events',
'ReplayGapless' => 'Gapless Events',
'ReplaySingle' => 'Single Event',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Reset',
'ResetEventCounts' => 'Borrar Contador Eventos',
'Restart' => 'Reiniciar',
@ -561,6 +595,7 @@ $SLANG = array(
'Save' => 'Guardar',
'SaveAs' => 'Guardar Como',
'SaveFilter' => 'Guardar Filtro',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Escala',
'Score' => 'Res.',
'Secs' => 'Seg',
@ -577,6 +612,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Abrir Filtro',
'ShowTimeline' => 'Show Timeline',
'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Size',
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
'Sleep' => 'Sleep',
@ -596,6 +632,10 @@ $SLANG = array(
'State' => 'Estado',
'Stats' => 'Est.',
'Status' => 'Estado',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Step',
'StepBack' => 'Step Back',
'StepForward' => 'Step Forward',
@ -606,6 +646,8 @@ $SLANG = array(
'Stills' => 'Fotos',
'Stop' => 'Desactivar',
'Stopped' => 'Apagado',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Submit' => 'Submit',
@ -625,6 +667,7 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => 'Etiqueta Hora',
'TimestampLabelFormat' => 'Formato Etiqueta Hora',
'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30
'TimestampLabelX' => 'Eje X Etiqueta Hora',
'TimestampLabelY' => 'Eje Y Etiqueta Hora',
'Today' => 'Today',
@ -671,6 +714,7 @@ $SLANG = array(
'VideoGenParms' => 'Parametros Generacion Video',
'VideoGenSucceeded' => 'Video Generation Succeeded!',
'VideoSize' => 'Tamaño Video',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Ver',
'ViewAll' => 'Ver Todo',
'ViewEvent' => 'View Event',
@ -680,6 +724,7 @@ $SLANG = array(
'Watch' => 'Monitor',
'Web' => 'Web',
'WebColour' => 'Web Colour',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Semana',
'White' => 'White',
'WhiteBalance' => 'White Balance',

View File

@ -78,6 +78,8 @@ $SLANG = array(
'Actual' => 'Actual',
'AddNewControl' => 'Añadir nuevo control',
'AddNewMonitor' => 'Añadir nuevo monitor',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Añadir nuevo usuario',
'AddNewZone' => 'Añadir nueva zona',
'Alarm' => 'Alarma',
@ -105,22 +107,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Estado de archivo',
'AttrAvgScore' => 'Promed. señal',
'AttrCause' => 'Causa',
'AttrDate' => 'Fecha',
'AttrDateTime' => 'Fecha/Hora',
'AttrDiskBlocks' => 'Bloques del disco',
'AttrDiskPercent' => 'Porcentaje del disco',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Duración',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Marcos',
'AttrId' => 'Id',
'AttrMaxScore' => 'Señal máxima',
'AttrMonitorId' => 'Id monitor',
'AttrMonitorName' => 'Nombre del monitor',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Nombre',
'AttrNotes' => 'Notas',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'Carga del sistema',
'AttrTime' => 'Hora',
'AttrTotalScore' => 'Señal total',
'AttrWeekday' => 'Día de la semana',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Autodetener tiempo de espera',
'Available' => 'Disponible',
@ -153,9 +165,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'El porcentaje de la referencia de mezcla debe ser un entero positivo',
'BadSectionLength' => 'La duración de la sección debe ser un entero de 30 o más',
'BadSignalCheckColour' => 'El color de verificación de señal debe ser una cadena de color RGB válida',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer' => 'La secuencia de búfer de reproducción debe ser un entero de cero o más',
'BadWarmupCount' => 'Los marcos de calentamiento deben ser un entero de cero o más',
'BadWebColour' => 'El color web debe ser una cadena de color web válida',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'El ancho debe tener un valor válido',
'Bandwidth' => 'Ancho de banda',
'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -219,10 +233,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Elegir selección de registro',
'ChoosePreset' => 'Elegir preprogramación',
'Clear' => 'Limpiar',
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Cerrar',
'Colour' => 'Color',
'Command' => 'Comando',
'Component' => 'Componente',
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config',
'ConfiguredFor' => 'Configurado para',
'ConfirmDeleteEvents' => '¿Seguro que desea borrar los eventos seleccionados?',
@ -280,9 +296,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Aún no, recordarme de nuevo en 1 semana',
'DonateYes' => 'Sí, me gustaría hacer una donación ahora',
'Download' => 'Descargar',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicar nombre de monitor',
'Duration' => 'Duración',
'Edit' => 'Editar',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Habilitar alarmas',
'Enabled' => 'Habilitado',
@ -299,6 +317,7 @@ $SLANG = array(
'Events' => 'Eventos',
'Exclude' => 'Excluir',
'Execute' => 'Ejecutar',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exportar',
'ExportDetails' => 'Exportar detalles de evento',
'ExportFailed' => 'Fallo al exportar',
@ -328,8 +347,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Ejecutar comando para todas las coincidencias',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => 'Detalles de mensaje de todas las coincidencias',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtrar Px',
'FilterUnset' => 'Debe especificar un ancho y un alto para el filtro',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Subir todas las coincidencias',
'FilterVideoEvents' => 'Create video for all matches', // Added - 2011-08-23
'Filters' => 'Filtros',
@ -354,6 +375,7 @@ $SLANG = array(
'Function' => 'Función',
'Gain' => 'Ganancia',
'General' => 'General',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Generate Video', // Added - 2011-08-23
'GeneratingVideo' => 'Generating Video', // Added - 2011-08-23
'GoToZoneMinder' => 'Ir a ZoneMinder.com',
@ -374,6 +396,7 @@ $SLANG = array(
'High' => 'Alto',
'HighBW' => 'Alto&nbsp;B/B',
'Home' => 'Inicio',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Hora',
'Hue' => 'Matiz',
'Id' => 'Id',
@ -398,6 +421,7 @@ $SLANG = array(
'Line' => 'Línea',
'LinkedMonitors' => 'Monitores enlazados',
'List' => 'Lista',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Carga',
'Local' => 'Local',
'Log' => 'Registro',
@ -484,6 +508,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'La lista de debajo muestra las cámaras analógicas y en red detectadas y si ya están siendo usadas o están disponibles para seleccionar.<br/><br/>Seleccione la entrada deseada de la lista de debajo.<br/><br/>Por favor tenga en cuenta que podrían no detectarse todas las cámaras y que elegir una cámara aquí podría sobrescribir cualquier valor que ya hubiera configurado para el monitor actual.<br/><br/>',
'Monitors' => 'Monitores',
'Montage' => 'Montaje',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Mes',
'More' => 'Más',
'MotionFrameSkip' => 'Motion Frame Skip',
@ -510,6 +535,7 @@ $SLANG = array(
'Next' => 'Siguiente',
'No' => 'No',
'NoDetectedCameras' => 'No se detectaron cámaras',
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'No hay marcos grabados para este evento',
'NoGroup' => 'Sin grupo',
'NoSavedFilters' => 'No hay filtros guardados',
@ -528,6 +554,8 @@ $SLANG = array(
'OpGt' => 'mayor que',
'OpGtEq' => 'mayor que o igual a',
'OpIn' => 'en conjunto',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'menor que',
'OpLtEq' => 'menor que o igual a',
'OpMatches' => 'coincidencias',
@ -537,6 +565,7 @@ $SLANG = array(
'Open' => 'Abrir',
'OptionHelp' => 'Ayuda de la opción',
'OptionRestartWarning' => 'Estos cambios podrían no surtir un efecto completo mientras el sistema esté ejecutándose. Cuando haya terminado haciendo cambios por favor asegúrese de reiniciar ZoneMinder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Opciones',
'OrEnterNewName' => 'o introduzca un nuevo nombre',
'Order' => 'Orden',
@ -574,9 +603,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocolo',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Valorar',
'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Grabar',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge',
'Refresh' => 'Refrescar',
'Remote' => 'Remoto',
@ -592,6 +625,7 @@ $SLANG = array(
'ReplayAll' => 'Todos los eventos',
'ReplayGapless' => 'Eventos sin espacios',
'ReplaySingle' => 'Evento individual',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Restablecer',
'ResetEventCounts' => 'Restablecer número de eventos',
'Restart' => 'Reiniciar',
@ -610,6 +644,7 @@ $SLANG = array(
'Save' => 'Guardar',
'SaveAs' => 'Guardar cómo',
'SaveFilter' => 'Guardar filtro',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Escalar',
'Score' => 'Cuenta',
'Secs' => 'Segs',
@ -626,6 +661,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Mostrar ventana de filtros',
'ShowTimeline' => 'Mostrar línea de tiempo',
'SignalCheckColour' => 'Color de comprobación de señal',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Tamaño',
'SkinDescription' => 'Cambiar el tema por defecto para este ordenador',
'Sleep' => 'Dormir',
@ -645,6 +681,10 @@ $SLANG = array(
'State' => 'Estado',
'Stats' => 'Estadísticas',
'Status' => 'Estado',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Salto',
'StepBack' => 'Salto atrás',
'StepForward' => 'Salto adelante',
@ -655,6 +695,8 @@ $SLANG = array(
'Stills' => 'Fijas',
'Stop' => 'Detener',
'Stopped' => 'Detenido',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Corriente',
'StreamReplayBuffer' => 'Secuencia de búfer de reproducción',
'Submit' => 'Enviar',
@ -674,9 +716,9 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => 'Marca de tiempo',
'TimestampLabelFormat' => 'Formato de hora multinacional',
'TimestampLabelSize' => 'Tamaño de fuente',
'TimestampLabelX' => 'Etiqueta de tiempo X',
'TimestampLabelY' => 'Etiqueta de tiempo Y',
'TimestampLabelSize' => 'Tamaño de fuente',
'Today' => 'Hoy',
'Tools' => 'Herramientas',
'Total' => 'Total',
@ -721,6 +763,7 @@ $SLANG = array(
'VideoGenParms' => 'Video Generation Parameters', // Added - 2011-08-23
'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2011-08-23
'VideoSize' => 'Video Size', // Added - 2011-08-23
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Ver',
'ViewAll' => 'Ver todos',
'ViewEvent' => 'Ver evento',
@ -730,6 +773,7 @@ $SLANG = array(
'Watch' => 'Observar',
'Web' => 'Web',
'WebColour' => 'Color web',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Semana',
'White' => 'Blanco',
'WhiteBalance' => 'Balance de blancos',

View File

@ -85,6 +85,8 @@ $SLANG = array(
'Actual' => 'Aktuaalne',
'AddNewControl' => 'Lisa uus Kontroll',
'AddNewMonitor' => 'Lisa uus Monitor',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Lisa uus Kasutaja',
'AddNewZone' => 'Lisa uus Tsoon',
'Alarm' => 'Alarm',
@ -112,22 +114,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Arhiivi Staatus',
'AttrAvgScore' => 'Keskm. Skoor',
'AttrCause' => 'Põhjus',
'AttrDate' => 'Kp.',
'AttrDateTime' => 'Kp/Kellaaeg',
'AttrDiskBlocks' => 'Ketta Blokk',
'AttrDiskPercent' => 'Ketta Protsent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Kestvus',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Kaadrid',
'AttrId' => 'Id',
'AttrMaxScore' => 'Maks. Skoor',
'AttrMonitorId' => 'Monitori Id',
'AttrMonitorName' => 'Monitori Nimi',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Nimi',
'AttrNotes' => 'Märkmed',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'Süsteemi Koormus',
'AttrTime' => 'Kellaaeg',
'AttrTotalScore' => 'Skoor Kokku',
'AttrWeekday' => 'Tööpäevad',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto Stop Ajalimiit',
'Available' => 'Saadaval',
@ -160,9 +172,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer',
'BadSectionLength' => 'Section length must be an integer of 30 or more',
'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more',
'BadWarmupCount' => 'Warmup frames must be an integer of zero or more',
'BadWebColour' => 'Web colour must be a valid web colour string',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'Width must be set to a valid value',
'Bandwidth' => 'Ribalaius',
'BandwidthHead' => 'Ribalaius', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -226,10 +240,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => 'Choose Preset',
'Clear' => 'Clear', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Sule',
'Colour' => 'Värv',
'Command' => 'Käsk',
'Component' => 'Komponent', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Seadistus',
'ConfiguredFor' => 'Seadistatud',
'ConfirmDeleteEvents' => 'Oled sa kindel kustamaks valitud sündmused?',
@ -287,9 +303,11 @@ $SLANG = array(
'DonateRemindWeek' => 'EI veel, tuleta meelde nädala pärast',
'DonateYes' => 'Jah, Ma soovin annetada',
'Download' => 'Lae alla',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Dubleeri Monitori Nimi',
'Duration' => 'Kestvus',
'Edit' => 'Muuda',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Luba Alarmid',
'Enabled' => 'Lubatud',
@ -306,6 +324,7 @@ $SLANG = array(
'Events' => 'Sündmuseid',
'Exclude' => 'Jäta välja',
'Execute' => 'Käivita',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Eksport',
'ExportDetails' => 'Ekspordi Sündmuste Detailid',
'ExportFailed' => 'Eksportimine Ebaõnnestus',
@ -335,8 +354,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Execute command on all matches',
'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => 'Message details of all matches',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter Px',
'FilterUnset' => 'You must specify a filter width and height',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Upload all matches',
'FilterVideoEvents' => 'Create video for all matches',
'Filters' => 'Filtrid',
@ -361,6 +382,7 @@ $SLANG = array(
'Function' => 'Funktsioon',
'Gain' => 'Gain',
'General' => 'Peamine',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Genereeri Video',
'GeneratingVideo' => 'Genereerin Videot',
'GoToZoneMinder' => 'Mine ZoneMinder.com',
@ -381,6 +403,7 @@ $SLANG = array(
'High' => 'Suurim',
'HighBW' => 'High&nbsp;B/W',
'Home' => 'Koju',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Tunnis',
'Hue' => 'Hue',
'Id' => 'Id',
@ -405,6 +428,7 @@ $SLANG = array(
'Line' => 'Line', // Added - 2011-06-16
'LinkedMonitors' => 'Lingitud monitorid',
'List' => 'List',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Koormus',
'Local' => 'Local',
'Log' => 'Logi', // Added - 2011-06-16
@ -491,6 +515,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>',
'Monitors' => 'Monitors',
'Montage' => 'Montage',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Kuus',
'More' => 'Veel', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip',
@ -517,6 +542,7 @@ $SLANG = array(
'Next' => 'Järgmine',
'No' => 'Ei',
'NoDetectedCameras' => 'Ei leidnud kaameraid',
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'Ei ole kaadreid salvetatud selles sündmuses',
'NoGroup' => 'Ei krupp',
'NoSavedFilters' => 'EiSalvestatudFiltreid',
@ -535,6 +561,8 @@ $SLANG = array(
'OpGt' => 'Suurem kui',
'OpGtEq' => 'suurem kui või võrdne',
'OpIn' => 'in set',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'vähem kui',
'OpLtEq' => 'vähem kui või võrdne',
'OpMatches' => 'klapib',
@ -544,6 +572,7 @@ $SLANG = array(
'Open' => 'Ava',
'OptionHelp' => 'Valik Aita',
'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Seaded',
'OrEnterNewName' => 'või sisesta uus nimi',
'Order' => 'Järjekord',
@ -581,9 +610,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .<br/><br/>Select the desired entry from the list below.<br/><br/>Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocol',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Rate',
'Real' => 'Reaaalne',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Salvesta',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Reference Image Blend %ge',
'Refresh' => 'Värskenda',
'Remote' => 'Remote',
@ -599,6 +632,7 @@ $SLANG = array(
'ReplayAll' => 'Kõik sündmused',
'ReplayGapless' => 'Lünkadeta sündmused',
'ReplaySingle' => 'Üksik sündmus',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Reset',
'ResetEventCounts' => 'Reset Event Counts',
'Restart' => 'Taaskäivita',
@ -617,6 +651,7 @@ $SLANG = array(
'Save' => 'Salvesta',
'SaveAs' => 'Salvesta kui',
'SaveFilter' => 'Salvesta Filter',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Skaala',
'Score' => 'Skoor',
'Secs' => 'Secs',
@ -633,6 +668,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Näita Filtri Akent',
'ShowTimeline' => 'Näita Timeline',
'SignalCheckColour' => 'Signaali Kontroll Värv',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Suurus',
'SkinDescription' => 'Vaheta veebilehe välimus selles arvutis', // Added - 2011-03-02
'Sleep' => 'Maga',
@ -652,6 +688,10 @@ $SLANG = array(
'State' => 'Olek',
'Stats' => 'Statistika',
'Status' => 'Staatus',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Samm',
'StepBack' => 'Samm tagasi',
'StepForward' => 'Samm edasi',
@ -662,6 +702,8 @@ $SLANG = array(
'Stills' => 'Stills',
'Stop' => 'Stop',
'Stopped' => 'Stopitud',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Striim',
'StreamReplayBuffer' => 'Striimi Replay Pildi Puhver',
'Submit' => 'Submit',
@ -681,6 +723,7 @@ $SLANG = array(
'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15.
'Timestamp' => 'Timestamp',
'TimestampLabelFormat' => 'Timestamp Label Format',
'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30
'TimestampLabelX' => 'Timestamp Label X',
'TimestampLabelY' => 'Timestamp Label Y',
'Today' => 'Täna',
@ -727,6 +770,7 @@ $SLANG = array(
'VideoGenParms' => 'Video Genereerimise Parameetrid',
'VideoGenSucceeded' => 'Video Genereerimine Õnnestus!!!',
'VideoSize' => 'Video Suurus',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Vaata',
'ViewAll' => 'View All',
'ViewEvent' => 'Vaata Sündmust',
@ -736,6 +780,7 @@ $SLANG = array(
'Watch' => 'Vaata',
'Web' => 'Veeb',
'WebColour' => 'Veebi värv',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Nädalas',
'White' => 'White',
'WhiteBalance' => 'White Balance',

View File

@ -84,6 +84,8 @@ $SLANG = array(
'Actual' => 'Réel',
'AddNewControl' => 'Ajouter contrôle',
'AddNewMonitor' => 'Ajouter caméra',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Ajouter utilisateur',
'AddNewZone' => 'Ajouter zone',
'Alarm' => 'Alarme',
@ -111,22 +113,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Etat Archive',
'AttrAvgScore' => 'Score moy.',
'AttrCause' => 'Cause',
'AttrDate' => 'Date',
'AttrDateTime' => 'Date/Heure',
'AttrDiskBlocks' => 'Blocs disque',
'AttrDiskPercent' => '% disque',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Durée',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFrames' => 'Images',
'AttrId' => 'Id',
'AttrMaxScore' => 'Score max.',
'AttrMonitorId' => 'N°',
'AttrMonitorName' => 'Nom caméra',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Nom',
'AttrNotes' => 'Notes',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30
'AttrSystemLoad' => 'Charge système',
'AttrTime' => 'Heure',
'AttrTotalScore' => 'Score total',
'AttrWeekday' => 'Semaine',
'Auto' => 'Auto',
'AutoStopTimeout' => 'Temporisation arrêt',
'Available' => 'Disponibles', // Added - 2009-03-31
@ -159,9 +171,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'Le pourcentage de fusion de l\'image de référence doit être un entier supérieur à 0 et inférieur à 100',
'BadSectionLength' => 'La longueur de la section doit être un entier supérieur ou égal à 30',
'BadSignalCheckColour' => 'La chaîne de caractères pour la couleur d\'état du signal est invalide',
'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30
'BadStreamReplayBuffer'=> 'Le tampon d\'images pour la relecture doit être un entier supérieur ou égal à 0',
'BadWarmupCount' => 'Le nombre d\'images tests doit être un entier supérieur ou égal à 0',
'BadWebColour' => 'La chaîne de caractères pour la couleur web est invalide',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWidth' => 'La valeur de la largeur est invalide',
'Bandwidth' => 'Débit',
'BandwidthHead' => 'Débit', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing
@ -225,10 +239,12 @@ $SLANG = array(
'ChooseLogSelection' => 'Choisir une sélection de journaux', // Added - 2011-06-17
'ChoosePreset' => 'Choisir préréglage',
'Clear' => 'Effacer', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Fermer',
'Colour' => 'Couleur',
'Command' => 'Commande',
'Component' => 'Composant', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config',
'ConfiguredFor' => 'Configuré pour',
'ConfirmDeleteEvents' => 'Etes-vous sûr de vouloir effacer le(s) événement(s) sélectionné(s)?',
@ -286,9 +302,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Pas encore, me rappeler dans 1 semaine',
'DonateYes' => 'Oui, je souhaiterais faire un don maintenant',
'Download' => 'Télécharger',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Dupliquer le nom de la caméra', // Added - 2009-03-31
'Duration' => 'Durée',
'Edit' => 'Editer',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email',
'EnableAlarms' => 'Activer les alarmes',
'Enabled' => 'Activé',
@ -305,6 +323,7 @@ $SLANG = array(
'Events' => 'Evénements',
'Exclude' => 'Exclure',
'Execute' => 'Exécuter',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exporter',
'ExportDetails' => 'Exporter détails événements',
'ExportFailed' => 'Exportation échouée',
@ -334,8 +353,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Exécuter une commande',
'FilterLog' => 'Filtre', // Added - 2015-04-18
'FilterMessageEvents' => 'Envoyer les détails par message',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtre Px',
'FilterUnset' => 'Vous devez spécifier une largeur et une hauteur de filtre',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Transférer',
'FilterVideoEvents' => 'Créer vidéo',
'Filters' => 'Filtres',
@ -360,6 +381,7 @@ $SLANG = array(
'Function' => 'Mode',
'Gain' => 'Gain',
'General' => 'Général',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Générer vidéo',
'GeneratingVideo' => 'Génération vidéo',
'GoToZoneMinder' => 'Aller sur ZoneMinder.com',
@ -380,6 +402,7 @@ $SLANG = array(
'High' => 'Haut',
'HighBW' => 'Haut débit',
'Home' => 'Maison',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Heure',
'Hue' => 'Teinte',
'Id' => 'N°',
@ -404,6 +427,7 @@ $SLANG = array(
'Line' => 'Ligne', // Added - 2011-06-16
'LinkedMonitors' => 'Caméra(s) liée(s)',
'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Charge',
'Local' => 'Local',
'Log' => 'Journal', // Added - 2011-06-16
@ -490,6 +514,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'La liste ci-dessous montre les caméras détectées localement ou sur le réseau, qu\'elles soient déjà configurées ou non.<br/><br/>Sélectionnez la caméra désirée dans la liste.<br/><br/>Veuillez noter que toutes les caméras ne sont pas forcément détectées et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.<br/><br/>', // Added - 2009-03-31
'Monitors' => 'Caméras',
'Montage' => 'Montage',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Mois',
'More' => 'Plus', // Added - 2011-06-16
'MotionFrameSkip' => 'Saut image en alarme',
@ -516,6 +541,7 @@ $SLANG = array(
'Next' => 'Suivant',
'No' => 'Non',
'NoDetectedCameras' => 'Pas de caméras détectées', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'Pas d\'images enregistrées pour cet événement',
'NoGroup' => 'Pas de groupe',
'NoSavedFilters' => 'Pas de filtres sauvegardés',
@ -534,6 +560,8 @@ $SLANG = array(
'OpGt' => 'sup. à',
'OpGtEq' => 'plus grand ou égal à',
'OpIn' => 'en lot',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'inf. à',
'OpLtEq' => 'inf. ou égal à',
'OpMatches' => 'correspond',
@ -543,6 +571,7 @@ $SLANG = array(
'Open' => 'Ouvrir',
'OptionHelp' => 'Aide',
'OptionRestartWarning' => 'Ces changements peuvent nécessiter un redémarrage de ZoneMinder pour être pleinement opérationnels.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'Options' => 'Options',
'OrEnterNewName' => 'ou entrez nouv. nom',
'Order' => 'Ordre',
@ -580,9 +609,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'La liste ci-dessous montre les profils de flux existants pour la caméra sélectionnée.<br/><br/>Sélectionnez le profil désiré dans la liste ci-dessous.<br/><br/>Veuillez noter que ZoneMinder ne peut pas configurer de profils additionels et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Progression', // Added - 2015-04-18
'Protocol' => 'Protocole',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Vitesse',
'Real' => 'Réel',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Enregistrer',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => '% fusion image référence',
'Refresh' => 'Rafraîchir',
'Remote' => 'Distant',
@ -598,6 +631,7 @@ $SLANG = array(
'ReplayAll' => 'Tous les événements',
'ReplayGapless' => 'Rejouer sans blancs',
'ReplaySingle' => 'Rejouer seul',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'RàZ',
'ResetEventCounts' => 'RàZ compteur évts',
'Restart' => 'Redémarrer',
@ -616,6 +650,7 @@ $SLANG = array(
'Save' => 'Sauvegarder',
'SaveAs' => 'Sauvegarder sous',
'SaveFilter' => 'Sauvegarder filtre',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Echelle',
'Score' => 'Score',
'Secs' => 'Secs',
@ -632,6 +667,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Filtres',
'ShowTimeline' => 'Afficher chronologie',
'SignalCheckColour' => 'Couleur vérif. signal',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Taille',
'SkinDescription' => 'Remplacer le skin par défaut', // Added - 2011-01-30
'Sleep' => 'Veille',
@ -651,6 +687,10 @@ $SLANG = array(
'State' => 'Etat',
'Stats' => 'Stats',
'Status' => 'Statut',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'Step' => 'Pas',
'StepBack' => 'Reculer',
'StepForward' => 'Avancer',
@ -661,6 +701,8 @@ $SLANG = array(
'Stills' => 'Photos',
'Stop' => 'Arrêter',
'Stopped' => 'Arrêté',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Flux',
'StreamReplayBuffer' => 'Nb d\'image(s) pour relecture',
'Submit' => 'Soumettre',
@ -680,9 +722,9 @@ $SLANG = array(
'TimelineTip4' => 'Utilisez les contrôles ci-dessous pour faire un zoom arrière ou naviguer en arrière et avancer sur l\'intervalle de temps.', // Added 2013.08.15.
'Timestamp' => 'Horodatage',
'TimestampLabelFormat' => 'Format',
'TimestampLabelSize' => 'Taille de police',
'TimestampLabelX' => 'Coordonnée X',
'TimestampLabelY' => 'Coordonnée Y',
'TimestampLabelSize' => 'Taille de police',
'Today' => 'Aujourd\'hui',
'Tools' => 'Outils',
'Total' => 'Total', // Added - 2011-06-16
@ -727,6 +769,7 @@ $SLANG = array(
'VideoGenParms' => 'Paramètres génération vidéo',
'VideoGenSucceeded' => 'Vidéo générée avec succès !',
'VideoSize' => 'Taille vidéo',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Voir',
'ViewAll' => 'Tout voir',
'ViewEvent' => 'Voir événement',
@ -736,6 +779,7 @@ $SLANG = array(
'Watch' => 'Regarder',
'Web' => 'Web',
'WebColour' => 'Couleur web',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Semaine',
'White' => 'Blanc',
'WhiteBalance' => 'Balance des blancs',

Some files were not shown because too many files have changed in this diff Show More