Merge branch 'master' into add_export_to_filter

This commit is contained in:
Isaac Connor 2018-09-25 14:54:06 -04:00
commit 01f1ffb591
142 changed files with 5710 additions and 3543 deletions

View File

@ -1,6 +1,6 @@
You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas: You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas:
- The ZoneMinder IRC channel - irc.freenode.net #zoneminder - The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/)
- The [ZoneMinder Forum](https://forums.zoneminder.com/) - The [ZoneMinder Forum](https://forums.zoneminder.com/)
**Do not post feature or enhancement requests, general discussions or support questions here.** **Do not post feature or enhancement requests, general discussions or support questions here.**

View File

@ -3,6 +3,8 @@ ZoneMinder
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) [![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://zoneminder-chat.herokuapp.com)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
## Overview ## Overview

View File

@ -50,4 +50,4 @@ ZM_PATH_SWAP=@ZM_TMPDIR@
# Full path to optional arp binary # Full path to optional arp binary
# ZoneMinder will find the arp binary automatically on most systems # ZoneMinder will find the arp binary automatically on most systems
ZM_PATH_ARP=@ZM_PATH_ARP@ ZM_PATH_ARP="@ZM_PATH_ARP@"

View File

@ -517,6 +517,7 @@ CREATE TABLE `Monitors` (
`ArchivedEvents` int(10) default NULL, `ArchivedEvents` int(10) default NULL,
`ArchivedEventDiskSpace` bigint default NULL, `ArchivedEventDiskSpace` bigint default NULL,
`ZoneCount` TINYINT NOT NULL DEFAULT 0, `ZoneCount` TINYINT NOT NULL DEFAULT 0,
`Refresh` int(10) unsigned default NULL,
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
@ -580,7 +581,7 @@ DROP TABLE IF EXISTS `Stats`;
CREATE TABLE `Stats` ( CREATE TABLE `Stats` (
`MonitorId` int(10) unsigned NOT NULL default '0', `MonitorId` int(10) unsigned NOT NULL default '0',
`ZoneId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0',
`EventId` int(10) unsigned NOT NULL default '0', `EventId` BIGINT UNSIGNED NOT NULL,
`FrameId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0',
`PixelDiff` tinyint(3) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0',
`AlarmPixels` int(10) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0',
@ -778,6 +779,10 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,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,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); INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,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,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);
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,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,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,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,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,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,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,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,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,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,'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,'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 -- Add some monitor preset values
-- --
@ -819,6 +824,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, 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,'/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,'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,'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,'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); 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 -- 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' ; CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF( SET @s = (SELECT IF(
@ -16,7 +16,7 @@ SET @s = (SELECT IF(
AND column_name = 'Refresh' AND column_name = 'Refresh'
) > 0, ) > 0,
"SELECT 'Column Refresh exists in Monitors'", "SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL" "ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
)); ));
PREPARE stmt FROM @s; PREPARE stmt FROM @s;

23
db/zm_update-1.31.45.sql Normal file
View File

@ -0,0 +1,23 @@
--
-- This updates a 1.31.44 database to 1.31.45
--
-- Add WebSite enum to Monitor.Type
-- Add Refresh column to Monitors table
-- This is the same as the update to 1.31.43, but due to Refresh not being added to zm_create.sql.in we need to have it
-- again in order to fix people who did a fresh install from 1.31.43 or 1.31.44.
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

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

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

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
--

View File

@ -37,11 +37,13 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libphp-serialization-perl , libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl , libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-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 , 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 , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl , libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl , libdata-uuid-perl
,libnumber-bytes-human-perl
,libfile-slurp-perl
, libpcre3 , libpcre3
, ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57 , ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57
, rsyslog | system-log-daemon , 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. # Display a message to show the RHEL build options are being processed.
if(ZM_TARGET_DISTRO MATCHES "^el") 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.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.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/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") else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) 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(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @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") endif(ZM_WEB_USER STREQUAL "nginx")
# Create several empty folders # Create several empty folders

View File

@ -72,7 +72,7 @@ New installs
6. Configure the web server 6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal, 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. HTTP will auto-redirect to HTTPS.
Inspect the web server configuration file and verify it meets your needs: Inspect the web server configuration file and verify it meets your needs:
@ -129,7 +129,7 @@ New installs
Upgrades 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 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 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 /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. 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. 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. 4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command: Most upgrades can be performed by executing the following command:

View File

@ -72,7 +72,7 @@ New installs
6. Configure the web server 6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal, 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. HTTP will auto-redirect to HTTPS.
Inspect the web server configuration file and verify it meets your needs: Inspect the web server configuration file and verify it meets your needs:
@ -129,7 +129,7 @@ New installs
Upgrades 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 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 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 /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 3. Verify the ZoneMinder Apache configuration file in the folder
/etc/zm/www. You will have a file called "zoneminder.conf" and there /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. 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. 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. 4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command: Most upgrades can be performed by executing the following command:

View File

@ -22,11 +22,10 @@
%global with_apcu_bc 1 %global with_apcu_bc 1
%endif %endif
%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.31.44 Version: 1.32.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons
@ -203,8 +202,8 @@ fi
%{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || : %{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || :
# Warn the end user to read the README file # 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 "\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: %{_docdir}/%{name}\n" echo -e "\nThe README file is located here: %{_pkgdocdir}/README\n"
%if 0%{?with_nginx} %if 0%{?with_nginx}
# Nginx does not create an SSL certificate like the apache package does so lets do that here # Nginx does not create an SSL certificate like the apache package does so lets do that here
@ -252,7 +251,7 @@ EOF
%files %files
%license COPYING %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 # We want these two folders to have "normal" read permission
# compared to the folder contents # compared to the folder contents
@ -321,11 +320,32 @@ EOF
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog %changelog
* Sun Apr 22 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.31.42-1 * Wed Sep 12 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.0-1
- Remove support for sysvinit a.k.a. el6 - 1.32.0 release
- use desktop-file-install for new zoneminder.desktop file - remove el6 (sys v init) support
- add new web cache folder - Make README name consistent across all supported distros
- 1.31.42 development snapshot - 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 * Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1
- modify autosetup macro parameters - modify autosetup macro parameters

View File

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

View File

@ -32,7 +32,7 @@ Package: libzoneminder-perl
Section: perl Section: perl
Architecture: all Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, 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 libsys-mmap-perl, liburi-encode-perl, libwww-perl
Description: Perl libraries for ZoneMinder Description: Perl libraries for ZoneMinder
ZoneMinder is a video camera security and surveillance solution. ZoneMinder is a video camera security and surveillance solution.

View File

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

View File

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

View File

@ -29,13 +29,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: 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** **Login process for older versions of ZoneMinder**

View File

@ -150,7 +150,7 @@ Orientation
WebSite 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 Website URL
Enter the full http or https url to the desired website. 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_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. 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. 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 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>/ 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. 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.

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

@ -0,0 +1,17 @@
# This configuration is only needed for compatibility with zmninja on iOS
# 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.
#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; package ZoneMinder::Base;
use 5.006; use 5.006;
@ -82,11 +58,18 @@ Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE =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 This program is distributed in the hope that it will be useful,
it under the same terms as Perl itself, either Perl version 5.8.3 or, but WITHOUT ANY WARRANTY; without even the implied warranty of
at your option, any later version of Perl 5 you may have available. 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 =cut

View File

@ -149,7 +149,7 @@ BEGIN {
foreach my $str ( <$CONFIG> ) { foreach my $str ( <$CONFIG> ) {
next if ( $str =~ /^\s*$/ ); next if ( $str =~ /^\s*$/ );
next if ( $str =~ /^\s*#/ ); next if ( $str =~ /^\s*#/ );
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/;
if ( ! $name ) { if ( ! $name ) {
print( STDERR "Warning, bad line in $config_file: $str\n" ); print( STDERR "Warning, bad line in $config_file: $str\n" );
next; next;

View File

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

@ -0,0 +1,475 @@
# ==========================================================================
#
# ZoneMinder Dericam P2 Control Protocol Module
# Copyright (C) Roman Dissertori
#
# 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 implementation of the Dericam P2 device control protocol
#
package ZoneMinder::Control::DericamP2;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our %CamParams = ();
# ==========================================================================
#
# Dericam P2 Control Protocol
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:@10.1.2.1:80
# zoneminder:zonepass@10.0.100.1:40000
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
Info( "http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=getimageattr";
my $req = $self->sendCmd( $cmd );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
# Parse results setting values in %FCParams
my $content = $res->decoded_content;
while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $stop_command = shift;
my $autostop = shift;
if( $stop_command && $autostop)
{
Debug( "Auto Stop" );
usleep( $autostop );
my $cmd = "decoder_control.cgi?command=".$stop_command;
$self->sendCmd( $cmd );
}
}
# Reset the Camera
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
# Move to default position
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=home";
$self->sendCmd( $cmd );
# Reset all other values to default
$cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-image_type=1&-default=on";
$self->sendCmd( $cmd );
}
# Reboot Camera (on Sleep button)
sub sleep
{
my $self = shift;
Debug( "Camera Reboot" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=sysreboot";
$self->sendCmd( $cmd );
}
# Stop the Camera
sub moveStop
{
my $self = shift;
Debug( "Camera Stop" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-act=stop";
$self->sendCmd( $cmd );
}
#Up Arrow
sub moveConUp
{
my $self = shift;
my $stop_command = "1";
Debug( "Move Up" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=up&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Down Arrow
sub moveConDown
{
my $self = shift;
my $stop_command = "3";
Debug( "Move Down" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=down&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Left Arrow
sub moveConLeft
{
my $self = shift;
my $stop_command = "5";
Debug( "Move Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=left&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Right Arrow
sub moveConRight
{
my $self = shift;
my $stop_command = "7";
Debug( "Move Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=right&-speed=45";
$self->sendCmd( $cmd );
#$self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom In
sub zoomConTele
{
my $self = shift;
Debug( "Zoom Tele" );
my $cmd = "decoder_control.cgi?command=18";
$self->sendCmd( $cmd );
}
#Zoom Out
sub zoomConWide
{
my $self = shift;
Debug( "Zoom Wide" );
my $cmd = "decoder_control.cgi?command=16";
$self->sendCmd( $cmd );
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=upright&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
my $self = shift;
Debug( "Move Diagonally Down Right" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=downright&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
my $self = shift;
Debug( "Move Diagonally Up Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=upleft&-speed=45";
$self->sendCmd( $cmd );
}
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
my $self = shift;
Debug( "Move Diagonally Down Left" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=downnleft&-speed=45";
$self->sendCmd( $cmd );
}
#Set Camera Preset
#Presets must be translated into values internal to the camera
#Those values are: 30,32,34,36,38,40,42,44 for presets 1-8 respectively
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=preset&-act=set&-number=".(($preset*2) + 28);
$self->sendCmd( $cmd );
}
}
#Recall Camera Preset
#Presets must be translated into values internal to the camera
#Those values are: 31,33,35,37,39,41,43,45 for presets 1-8 respectively
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
if (( $preset >= 1 ) && ( $preset <= 8 )) {
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=preset&-act=goto&-number=".(($preset*2) + 29);
$self->sendCmd( $cmd );
}
if ( $preset == 9 ) {
$self->horizontalPatrol();
}
if ( $preset == 10 ) {
$self->verticalPatrol();
}
}
#Horizontal Patrol
sub horizontalPatrol
{
my $self = shift;
Debug( "Horizontal Patrol" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=hscan";
$self->sendCmd( $cmd );
}
#Vertical Patrol
sub verticalPatrol
{
my $self = shift;
Debug( "Vertical Patrol" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=0&-act=vscan";
$self->sendCmd( $cmd );
}
# Increase Brightness
sub irisAbsOpen
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} += $step;
$CamParams{'brightness'} = 100 if ($CamParams{'brightness'} > 100);
Debug( "Increase Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
# Decrease Brightness
sub irisAbsClose
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'brightness'} -= $step;
$CamParams{'brightness'} = 0 if ($CamParams{'brightness'} < 0);
Debug( "Decrease Brightness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-brightness=".$CamParams{'brightness'};
$self->sendCmd( $cmd );
}
# Increase Contrast
sub whiteAbsIn
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} += $step;
$CamParams{'contrast'} = 100 if ($CamParams{'contrast'} > 100);
Debug( "Increase Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
# Decrease Contrast
sub whiteAbsOut
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'contrast'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'contrast'} -= $step;
$CamParams{'contrast'} = 0 if ($CamParams{'contrast'} < 0);
Debug( "Decrease Contrast" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-contrast=".$CamParams{'contrast'};
$self->sendCmd( $cmd );
}
#TODO Saturation cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=44 [0-255]
sub satIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} += $step;
$CamParams{'saturation'} = 255 if ($CamParams{'saturation'} > 255);
Debug( "Increase Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
sub satDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'saturation'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'saturation'} -= $step;
$CamParams{'saturation'} = 0 if ($CamParams{'saturation'} < 0);
Debug( "Decrease Saturation" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-saturation=".$CamParams{'saturation'};
$self->sendCmd( $cmd );
}
#TODO Sharpness cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=37 [0-100]
sub sharpIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} += $step;
$CamParams{'sharpness'} = 100 if ($CamParams{'sharpness'} > 100);
Debug( "Increase Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
sub sharpDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'sharpness'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'sharpness'} -= $step;
$CamParams{'sharpness'} = 0 if ($CamParams{'sharpness'} < 0);
Debug( "Decrease Sharpness" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-sharpness=".$CamParams{'sharpness'};
$self->sendCmd( $cmd );
}
#TODO Hue cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=37 [0-100]
sub hueIncrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} += $step;
$CamParams{'hue'} = 100 if ($CamParams{'hue'} > 100);
Debug( "Increase Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
sub hueDecrease
{
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'hue'});
my $step = $self->getParam( $params, 'step' );
$CamParams{'hue'} -= $step;
$CamParams{'hue'} = 0 if ($CamParams{'hue'} < 0);
Debug( "Decrease Hue" );
my $cmd = "cgi-bin/hi3510/param.cgi?cmd=setimageattr&-hue=".$CamParams{'hue'};
$self->sendCmd( $cmd );
}
1;

View File

@ -74,39 +74,36 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub open sub open {
{
my $self = shift; my $self = shift;
$self->loadMonitor(); $self->loadMonitor();
use LWP::UserAgent; use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new; $self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'open'; $self->{state} = 'open';
} }
sub printMsg sub printMsg {
{
my $self = shift; my $self = shift;
my $msg = shift; my $msg = shift;
my $msg_len = length($msg); my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" ); Debug($msg.'['.$msg_len.']');
} }
sub sendCmd sub sendCmd {
{
my $self = shift; my $self = shift;
my $cmd = shift; my $cmd = shift;
my $msg = shift; my $msg = shift;
my $content_type = shift; my $content_type = shift;
my $result = undef; my $result = undef;
printMsg( $cmd, "Tx" ); printMsg($cmd, 'Tx');
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/$cmd"; my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd;
my $req = HTTP::Request->new(POST => $server_endpoint); my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => $content_type); $req->header('content-type' => $content_type);
$req->header('Host' => $self->{Monitor}->{ControlAddress}); $req->header('Host' => $self->{Monitor}->{ControlAddress});
@ -122,14 +119,13 @@ sub sendCmd
} else { } else {
Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'"); Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'");
} }
return( $result ); return $result;
} }
sub getCamParams sub getCamParams {
{
my $self = shift; my $self = shift;
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>'; my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging"; my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging';
my $req = HTTP::Request->new(POST => $server_endpoint); my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $self->{Monitor}->{ControlAddress}); $req->header('Host' => $self->{Monitor}->{ControlAddress});
@ -150,22 +146,19 @@ sub getCamParams
if ( $content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/ ) { if ( $content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/ ) {
$CamParams{$1} = $2; $CamParams{$1} = $2;
} }
} } else {
else
{
Error("Unable to retrieve camera image settings:'".$res->status_line()."'"); Error("Unable to retrieve camera image settings:'".$res->status_line()."'");
} }
} }
#autoStop #autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab #This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop sub autoStop {
{
my $self = shift; my $self = shift;
my $autostop = shift; my $autostop = shift;
if ( $autostop ) { if ( $autostop ) {
Debug( "Auto Stop" ); Debug('Auto Stop');
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>'; my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
@ -175,20 +168,18 @@ sub autoStop
} }
# Reset the Camera # Reset the Camera
sub reset sub reset {
{ Debug('Camera Reset');
Debug( "Camera Reset" );
my $self = shift; my $self = shift;
my $cmd = ""; my $cmd = '';
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>'; my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd($cmd, $msg, $content_type); $self->sendCmd($cmd, $msg, $content_type);
} }
#Up Arrow #Up Arrow
sub moveConUp sub moveConUp {
{ Debug('Move Up');
Debug( "Move Up" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -198,9 +189,8 @@ sub moveConUp
} }
#Down Arrow #Down Arrow
sub moveConDown sub moveConDown {
{ Debug('Move Down');
Debug( "Move Down" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -210,9 +200,8 @@ sub moveConDown
} }
#Left Arrow #Left Arrow
sub moveConLeft sub moveConLeft {
{ Debug('Move Left');
Debug( "Move Left" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -222,9 +211,8 @@ sub moveConLeft
} }
#Right Arrow #Right Arrow
sub moveConRight sub moveConRight {
{ Debug('Move Right');
Debug( "Move Right" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -234,9 +222,8 @@ sub moveConRight
} }
#Zoom In #Zoom In
sub zoomConTele sub zoomConTele {
{ Debug('Zoom Tele');
Debug( "Zoom Tele" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -246,9 +233,8 @@ sub zoomConTele
} }
#Zoom Out #Zoom Out
sub zoomConWide sub zoomConWide {
{ Debug('Zoom Wide');
Debug( "Zoom Wide" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -259,9 +245,8 @@ sub zoomConWide
#Diagonally Up Right Arrow #Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them #This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight sub moveConUpRight {
{ Debug('Move Diagonally Up Right');
Debug( "Move Diagonally Up Right" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -272,9 +257,8 @@ sub moveConUpRight
#Diagonally Down Right Arrow #Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them #This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight sub moveConDownRight {
{ Debug('Move Diagonally Down Right');
Debug( "Move Diagonally Down Right" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -285,9 +269,8 @@ sub moveConDownRight
#Diagonally Up Left Arrow #Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them #This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft sub moveConUpLeft {
{ Debug('Move Diagonally Up Left');
Debug( "Move Diagonally Up Left" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -298,9 +281,8 @@ sub moveConUpLeft
#Diagonally Down Left Arrow #Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them #This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft sub moveConDownLeft {
{ Debug('Move Diagonally Down Left');
Debug( "Move Diagonally Down Left" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
@ -310,9 +292,8 @@ sub moveConDownLeft
} }
#Stop #Stop
sub moveStop sub moveStop {
{ Debug('Move Stop');
Debug( "Move Stop" );
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
@ -321,8 +302,7 @@ sub moveStop
} }
#Set Camera Preset #Set Camera Preset
sub presetSet sub presetSet {
{
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
my $preset = $self->getParam($params, 'preset'); my $preset = $self->getParam($params, 'preset');
@ -334,8 +314,7 @@ sub presetSet
} }
#Recall Camera Preset #Recall Camera Preset
sub presetGoto sub presetGoto {
{
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
my $preset = $self->getParam($params, 'preset'); my $preset = $self->getParam($params, 'preset');
@ -348,45 +327,42 @@ sub presetGoto
#Horizontal Patrol #Horizontal Patrol
#To be determined if this camera supports this feature #To be determined if this camera supports this feature
sub horizontalPatrol sub horizontalPatrol {
{ Debug('Horizontal Patrol');
Debug( "Horizontal Patrol" );
my $self = shift; my $self = shift;
my $cmd = ''; my $cmd = '';
my $msg =''; my $msg ='';
my $content_type = ''; my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type ); # $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." ); Error('PTZ Command not implemented in control script.');
} }
#Horizontal Patrol Stop #Horizontal Patrol Stop
#To be determined if this camera supports this feature #To be determined if this camera supports this feature
sub horizontalPatrolStop sub horizontalPatrolStop {
{ Debug('Horizontal Patrol Stop');
Debug( "Horizontal Patrol Stop" );
my $self = shift; my $self = shift;
my $cmd = ''; my $cmd = '';
my $msg =''; my $msg ='';
my $content_type = ''; my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type ); # $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." ); Error('PTZ Command not implemented in control script.');
} }
# Increase Brightness # Increase Brightness
sub irisAbsOpen sub irisAbsOpen {
{ Debug("Iris $CamParams{Brightness}");
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
$self->getCamParams() unless($CamParams{'Brightness'}); $self->getCamParams() unless($CamParams{Brightness});
my $step = $self->getParam($params, 'step'); my $step = $self->getParam($params, 'step');
my $max = 100; my $max = 100;
$CamParams{'Brightness'} += $step; $CamParams{Brightness} += $step;
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max); $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
my $cmd = 'onvif/imaging'; my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type ); $self->sendCmd( $cmd, $msg, $content_type );
} }
@ -394,57 +370,55 @@ sub irisAbsOpen
# Decrease Brightness # Decrease Brightness
sub irisAbsClose sub irisAbsClose
{ {
Debug( "Iris $CamParams{'Brightness'}" ); Debug( "Iris $CamParams{Brightness}" );
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'}); $self->getCamParams() unless($CamParams{brightness});
my $step = $self->getParam( $params, 'step' ); my $step = $self->getParam( $params, 'step' );
my $min = 0; my $min = 0;
$CamParams{'Brightness'} -= $step; $CamParams{Brightness} -= $step;
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min); $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
my $cmd = 'onvif/imaging'; my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Brightness}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type ); $self->sendCmd( $cmd, $msg, $content_type );
} }
# Increase Contrast # Increase Contrast
sub whiteAbsIn sub whiteAbsIn {
{ Debug("Iris $CamParams{Contrast}");
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'}); $self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam( $params, 'step' ); my $step = $self->getParam( $params, 'step' );
my $max = 100; my $max = 100;
$CamParams{'Contrast'} += $step; $CamParams{Contrast} += $step;
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max); $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
my $cmd = 'onvif/imaging'; my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
} }
# Decrease Contrast # Decrease Contrast
sub whiteAbsOut sub whiteAbsOut {
{ Debug("Iris $CamParams{Contrast}");
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift; my $self = shift;
my $params = shift; my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'}); $self->getCamParams() unless($CamParams{Contrast});
my $step = $self->getParam($params, 'step'); my $step = $self->getParam($params, 'step');
my $min = 0; my $min = 0;
$CamParams{'Contrast'} -= $step; $CamParams{Contrast} -= $step;
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min); $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
my $cmd = 'onvif/imaging'; my $cmd = 'onvif/imaging';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>'; my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{Contrast}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
} }
1; 1;
__END__

View File

@ -1,504 +0,0 @@
# =========================================================================
#
# ZoneMinder Trendnet TV-IP862IC IP Control Protocol Module, $Date: $, $Revision: $
# Copyright (C) 2014 Vincent Giovannone
#
#
# ==========================================================================
#
# 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 implementation of the Trendnet TV-IP672PI IP camera control
# protocol. Also works or TV-IP862IC
#
# For Zoneminder 1.26+
#
# Under control capability:
#
# * Main: name it (suggest TVIP672PI), type is FFMPEG (or remote if you're using MJPEG), protocol is TVIP672PI
# * Main (more): Can wake, can sleep, can reset
# * Move: Can move, can move diagonally, can move mapped, can move relative
# * Pan: Can pan
# * Tilt: Can tilt
# * Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.)
#
# Under control tab in the monitor itself:
#
# * Controllable
# * Control type is the name you gave it in control capability above
# * Control device is the password you use to authenticate to the camera (see further below if you need to change the username from "admin")
# * Control address is the camera's ip address AND web port. example: 192.168.1.1:80
#
#
# If using with anything but a TV-IP672PI (ex: TV-IP672WI), YOU MUST MATCH THE REALM TO MATCH YOUR CAMERA FURTHER DOWN!
#
#
# Due to how the TVIP672 represents presets internally, you MUST define the presets in order... i.e. 1,2,3,4... not 1,10,3,4.
# (see much further down for why, if you care...)
#
package ZoneMinder::Control::TVIP862;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
#
# ******** YOU MUST CHANGE THE FOLLOWING LINES TO MATCH YOUR CAMERA! **********
#
# I assume that "TV-IP672WI" would work for the TV-IP672WI, but can't test since I don't own one.
#
# TV-IP672PI works for the PI version, of course.
#
# Finally, the username is the username you'd like to authenticate as.
#
our $REALM = 'TV-IP862IC';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
# ==========================================================================
#
# Trendnet TV-IP672PI Control Protocol
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
sub open
{
my $self = shift;
$self->loadMonitor();
my ( $protocol, $username, $password, $address )
= $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/;
if ( $username ) {
$USERNAME = $username;
$PASSWORD = $password;
$ADDRESS = $address;
} else {
Error( "Failed to parse auth from address");
$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} = 'open';
# 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 $req = HTTP::Request->new( GET=>"http://".$ADDRESS."/cgi/ptdc.cgi" );
my $res = $self->{ua}->request($req);
if ( ! $res->is_success ) {
Debug("Need newer REALM");
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
if ( $$headers{'www-authenticate'} ) {
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ( $tokens =~ /\w+="([^"]+)"/i ) {
$REALM = $1;
Debug( "Changing REALM to $REALM" );
$self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD);
} # end if
} else {
Debug("No headers line");
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
} # end if ! $res->is_success
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
# This routine is used for all moving, which are all GET commands...
my $self = shift;
my $cmd = shift;
my $result = undef;
my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd;
my $req = HTTP::Request->new( GET=>$url );
Debug ("sendCmd command: " . $url );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("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()."' cmd:'".$cmd."'" );
}
}
return( $result );
}
sub sendCmdPost
{
#
# This routine is used for setting/clearing presets and IR commands, which are POST commands...
#
my $self = shift;
my $url = shift;
my $cmd = shift;
my $result = undef;
if ($url eq undef)
{
Error ("url passed to sendCmdPost is undefined.");
return(-1);
}
Debug ("sendCmdPost url: " . $url . " cmd: " . $cmd);
my $req = HTTP::Request->new(POST => "http://".$ADDRESS.$url);
$req->content_type('application/x-www-form-urlencoded');
$req->content($cmd);
Debug ( "sendCmdPost credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "sendCmdPost Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} else {
Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
} # endif
}
return( $result );
}
sub move
{
my $self = shift;
my $panSteps = shift;
my $tiltSteps = shift;
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
$self->sendCmd( $cmd );
}
sub moveRelUpLeft
{
my $self = shift;
Debug( "Move Up Left" );
$self->move(-3, 3);
}
sub moveRelUp
{
my $self = shift;
Debug( "Move Up" );
$self->move(0, 3);
}
sub moveRelUpRight
{
my $self = shift;
Debug( "Move Up Right" );
$self->move(3, 3);
}
sub moveRelLeft
{
my $self = shift;
Debug( "Move Left" );
$self->move(-3, 0);
}
sub moveRelRight
{
my $self = shift;
Debug( "Move Right" );
$self->move(3, 0);
}
sub moveRelDownLeft
{
my $self = shift;
Debug( "Move Down Left" );
$self->move(-3, -3);
}
sub moveRelDown
{
my $self = shift;
Debug( "Move Down" );
$self->move(0, -3);
}
sub moveRelDownRight
{
my $self = shift;
Debug( "Move Down Right" );
$self->move(3, -3);
}
# moves the camera to center on the point that the user clicked on in the video image.
# This isn't mega accurate but good enough for most purposes
sub moveMap
{
# If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!)
# They scale the movement and are here to compensate for manufacturing variation.
# It's never going to be perfect, so just get somewhere in the ballpark and call it a day.
# (Don't forget to kill the zmcontrol process while tweaking!)
# 1280x800
my $hscale = 31;
my $vscale = 25;
# 1280x800 with fisheye
#my $hscale = 15;
#my $vscale = 15;
# 640x400
#my $hscale = 14;
#my $vscale = 12;
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale;
my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale;
$hor = int($hor);
$ver = -1 * int($ver);
Debug( "Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver" );
$self->move( $hor, $ver );
}
# **** PRESETS ****
#
# OK, presets work a little funky but they DO work, provided you define them in order and don't skip any.
#
# The problem is that when you load the web page for this camera, it gives a list of preset names tied to index numbers.
# So let's say you have four presets... A, B, C, and D, and defined them in that order.
# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell the camera to go to a preset, you actually tell it by number, not by name.
# (So "Go to D" is really "go to index 3".)
#
# Now let's say somebody deletes C via the camera's web GUI. The camera re-numbers the existing presets A=0, B=1, D=2.
# There's really no easy way for ZM to discover this re-numbering, so zoneminder would still send "go to preset 3" thinking
# it's telling the camera to go to point D. In actuality it's telling the camera to go to a preset that no longer exists.
#
# As long as you define your presets in order (i.e. define preset 1, then preset 2, then preset 3, etc.) everything will work just
# fine in ZoneMinder.
#
# (Home preset needs to be set via the camera's web gui, and is unaffected by any of this.)
#
# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only through!) ZM AND DON'T SKIP ANY.
#
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "presetName=$preset&command=del";
my $url = "/eng/admin/cam_control.cgi";
Debug ("presetClear: " . $preset . " cmd: " . $cmd);
$self->sendCmdPost($url,$cmd);
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "presetName=$preset&command=add";
my $url = "/eng/admin/cam_control.cgi";
Debug ("presetSet " . $preset . " cmd: " . $cmd);
$self->sendCmdPost ($url,$cmd);
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
$preset = $preset - 1;
Debug( "Goto Preset $preset" );
my $cmd = "goto_preset_position&index=$preset";
$self->sendCmd( $cmd );
}
sub presetHome
{
my $self = shift;
Debug( "Home Preset" );
my $cmd = "go_home";
$self->sendCmd( $cmd );
}
#
# **** IR CONTROLS ****
#
#
# Wake: Force IR on, always. (always night mode)
#
# Sleep: Force IR off, always. (always day mode)
#
# Reset: Automatic IR mode. (day/night mode determined by camera)
#
sub wake
{
# force IR on ("always night mode")
my $self = shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=3";
Debug("Wake -- IR on");
$self->sendCmdPost ($url,$cmd);
}
sub sleep
{
# force IR off ("always day mode")
my $self=shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=2";
Debug("Sleep -- IR off");
$self->sendCmdPost ($url,$cmd);
}
sub reset
{
# IR auto
my $self=shift;
my $url = "/eng/admin/adv_audiovideo.cgi";
my $cmd = "irMode=0";
Debug("Reset -- IR auto");
$self->sendCmdPost ($url,$cmd);
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for Trendnet TVIP672
=head1 SYNOPSIS
use ZoneMinder::Database;
stuff this in /usr/share/perl5/ZoneMinder/Control , then eat a sandwich
=head1 DESCRIPTION
Stub documentation for Trendnet TVIP672, created by Vince.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Read the comments at the beginning of this file to see the usage for zoneminder 1.25.0
=head1 AUTHOR
Vincent Giovannone, I'd rather you not email me.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2014 by Vincent Giovannone
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

@ -0,0 +1,431 @@
package ZoneMinder::Control::Trendnet;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
# You do not need to change the REALM, but you can get slightly faster response
# by setting so that the first auth request succeeds.
#
# The username and password should be passed in the ControlAddress field but you
# can set them here if you want.
#
our $REALM = '';
our $PROTOCOL = 'http://';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
sub open {
my $self = shift;
$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};
} 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 $res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi');
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);
$res = $self->{ua}->get($PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi');
if ( $res->is_success() ) {
$self->{state} = 'open';
return;
}
Error('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 {
Debug('No headers line');
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
} # end sub open
sub printMsg {
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug($msg.'['.$msg_len.']');
}
sub sendCmd {
# This routine is used for all moving, which are all GET commands...
my $self = shift;
my $cmd = shift;
my $url = $PROTOCOL.$ADDRESS.'/cgi/ptdc.cgi?command='.$cmd;
my $res = $self->{ua}->get($url);
Debug('sendCmd command: ' . $url);
if ( $res->is_success ) {
return !undef;
}
Error("Error check failed: '".$res->status_line()."' cmd:'".$cmd."'");
return;
}
sub sendCmdPost {
#
# This routine is used for setting/clearing presets and IR commands, which are POST commands...
#
my $self = shift;
my $url = shift;
my $form = shift;
my $result = undef;
if ( $url eq undef ) {
Error('url passed to sendCmdPost is undefined.');
return -1;
}
#Debug('sendCmdPost url: ' . $url . ' cmd: ' . $cmd);
my $res;
$res = $self->{ua}->post(
$PROTOCOL.$ADDRESS.$url,
Referer=>$PROTOCOL.$ADDRESS.$url,
Content=>$form
);
Debug("sendCmdPost credentials control to: $PROTOCOL$ADDRESS$url realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
if ( $res->is_success ) {
return !undef;
}
Error("sendCmdPost Error check failed: '".$res->status_line()."' cmd:");
return $result;
} # end sub sendCmdPost
sub move {
my $self = shift;
my $panSteps = shift;
my $tiltSteps = shift;
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
$self->sendCmd($cmd);
}
sub moveRelUpLeft {
my $self = shift;
Debug('Move Up Left');
$self->move(-3, 3);
}
sub moveRelUp {
my $self = shift;
Debug('Move Up');
$self->move(0, 3);
}
sub moveRelUpRight {
my $self = shift;
Debug('Move Up Right');
$self->move(3, 3);
}
sub moveRelLeft {
my $self = shift;
Debug('Move Left');
$self->move(-3, 0);
}
sub moveRelRight {
my $self = shift;
Debug('Move Right');
$self->move(3, 0);
}
sub moveRelDownLeft {
my $self = shift;
Debug('Move Down Left');
$self->move(-3, -3);
}
sub moveRelDown {
my $self = shift;
Debug('Move Down');
$self->move(0, -3);
}
sub moveRelDownRight {
my $self = shift;
Debug('Move Down Right');
$self->move(3, -3);
}
# moves the camera to center on the point that the user clicked on in the video image.
# This isn't mega accurate but good enough for most purposes
sub moveMap {
# If the camera moves too much, increase hscale and vscale. (...if it doesn't move enough, try decreasing!)
# They scale the movement and are here to compensate for manufacturing variation.
# It's never going to be perfect, so just get somewhere in the ballpark and call it a day.
# (Don't forget to kill the zmcontrol process while tweaking!)
# 1280x800
my $hscale = 31;
my $vscale = 25;
# 1280x800 with fisheye
#my $hscale = 15;
#my $vscale = 15;
# 640x400
#my $hscale = 14;
#my $vscale = 12;
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
my $hor = ($xcoord - ($self->{Monitor}->{Width} / 2))/$hscale;
my $ver = ($ycoord - ($self->{Monitor}->{Height} / 2))/$vscale;
$hor = int($hor);
$ver = -1 * int($ver);
Debug("Move Map to $xcoord,$ycoord, hor=$hor, ver=$ver");
$self->move($hor, $ver);
}
# **** PRESETS ****
#
# OK, presets work a little funky but they DO work, provided you define them
# in order and don't skip any.
#
# The problem is that when you load the web page for this camera, it gives a
# list of preset names tied to index numbers.
# So let's say you have four presets... A, B, C, and D, and defined them in
# that order.
# So A is index 0, B is index 1, C is index 2, D is index 3. When you tell
# the camera to go to a preset, you actually tell it by number, not by name.
# (So "Go to D" is really "go to index 3".)
#
# Now let's say somebody deletes C via the camera's web GUI. The camera
# re-numbers the existing presets A=0, B=1, D=2.
# There's really no easy way for ZM to discover this re-numbering, so
# zoneminder would still send "go to preset 3" thinking
# it's telling the camera to go to point D. In actuality it's telling the
# camera to go to a preset that no longer exists.
#
# As long as you define your presets in order (i.e. define preset 1, then
# preset 2, then preset 3, etc.) everything will work just
# fine in ZoneMinder.
#
# (Home preset needs to be set via the camera's web gui, and is unaffected by
# any of this.)
#
# So that's the limitation: DEFINE YOUR PRESETS IN ORDER THROUGH (and only
# through!) ZM AND DON'T SKIP ANY.
#
sub presetClear {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
my $cmd = "presetName=$preset&command=del";
my $url = '/eng/admin/cam_control.cgi';
Debug('presetClear: ' . $preset . ' cmd: ' . $cmd);
$self->sendCmdPost($url,{presetName=>$preset, command=>'del'});
}
sub presetSet {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
my $cmd = "presetName=$preset&command=add";
my $url = '/eng/admin/cam_control.cgi';
Debug('presetSet ' . $preset . ' cmd: ' . $cmd);
$self->sendCmdPost($url,{presetName=>$preset, command=>'add', Submit=>'Add'});
}
sub presetGoto {
my $self = shift;
my $params = shift;
my $preset = $self->getParam($params, 'preset');
$preset = $preset - 1;
Debug("Goto Preset $preset");
my $cmd = "goto_preset_position&index=$preset";
$self->sendCmd($cmd);
}
sub presetHome {
my $self = shift;
Debug('Home Preset');
my $cmd = 'go_home';
$self->sendCmd($cmd);
}
#
# **** IR CONTROLS ****
#
#
# Wake: Force IR on, always. (always night mode)
#
# Sleep: Force IR off, always. (always day mode)
#
# Reset: Automatic IR mode. (day/night mode determined by camera)
#
sub wake {
# force IR on ("always night mode")
my $self = shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=3';
Debug('Wake -- IR on');
$self->sendCmdPost($url,$cmd);
}
sub sleep {
# force IR off ("always day mode")
my $self = shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=2';
Debug('Sleep -- IR off');
$self->sendCmdPost($url,$cmd);
}
sub reset {
# IR auto
my $self=shift;
my $url = '/eng/admin/adv_audiovideo.cgi';
my $cmd = 'irMode=0';
Debug('Reset -- IR auto');
$self->sendCmdPost($url,$cmd);
}
1;
__END__
=head1 NAME
ZoneMinder::Control::Trendnet - Perl module for Trendnet cameras
=head1 SYNOPSIS
use ZoneMinder::Control::Trendnet;
place this in /usr/share/perl5/ZoneMinder/Control
=head1 DESCRIPTION
This module contains the implementation of the Trendnet # IP camera control
protocol. Has been tested with TV-IP862IC
Under control capability:
* Main: Can wake, can sleep, can reset
* Move: Can move, can move diagonally, can move mapped, can move relative
* Pan: Can pan
* Tilt: Can tilt
* Presets: Has presets, num presets 20, has home preset (don't set presets via camera's web server, only set via ZM.)
Under control tab in the monitor itself:
Controllable
Control type is the name you gave it in control capability above
Control address is the camera's ip address AND web port. example: 192.168.1.1:80
You can also put the authentication information here and change the
protocol to https using something like https://admin:password@192.168.1.1:80
=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

@ -184,16 +184,19 @@ sub zmDbGetMonitor {
my $id = shift; my $id = shift;
return( undef ) if ( !defined($id) ); if ( !defined($id) ) {
croak("Undefined id in zmDbgetMonitor");
return undef ;
}
my $sql = "select * from Monitors where Id = ?"; my $sql = 'SELECT * FROM Monitors WHERE Id = ?';
my $sth = $dbh->prepare_cached($sql) my $sth = $dbh->prepare_cached($sql)
or croak("Can't prepare '$sql': ".$dbh->errstr()); or croak("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($id) my $res = $sth->execute($id)
or croak("Can't execute '$sql': ".$sth->errstr()); or croak("Can't execute '$sql': ".$sth->errstr());
my $monitor = $sth->fetchrow_hashref(); my $monitor = $sth->fetchrow_hashref();
return( $monitor ); return $monitor;
} }
sub zmDbGetMonitorAndControl { sub zmDbGetMonitorAndControl {
@ -241,55 +244,31 @@ if ( ! defined $ac ) {
} # end if } # end if
$d->{AutoCommit} = $ac; $d->{AutoCommit} = $ac;
} # end sub end_transaction } # end sub end_transaction
1; 1;
__END__ __END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME =head1 NAME
ZoneMinder::Database - Perl extension for blah blah blah ZoneMinder::Database - Perl module containing database functions used in ZM
=head1 SYNOPSIS =head1 SYNOPSIS
use ZoneMinder::Database; use ZoneMinder::Database;
blah blah blah
=head1 DESCRIPTION =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 =head2 EXPORT
None by default. zmDbConnect
zmDbDisconnect
zmDbGetMonitors
zmDbGetMonitor
=head1 SEE ALSO zmDbGetMonitorAndControl
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.
=head1 AUTHOR =head1 AUTHOR
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt> 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 =cut

View File

@ -35,7 +35,6 @@ require Date::Manip;
require File::Find; require File::Find;
require File::Path; require File::Path;
require File::Copy; require File::Copy;
require File::Slurp;
require File::Basename; require File::Basename;
require Number::Bytes::Human; require Number::Bytes::Human;
@ -239,7 +238,7 @@ sub LinkPath {
'.'.$$event{Id} '.'.$$event{Id}
); );
} elsif ( $$event{Path} ) { } 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}; $$event{LinkPath} = $1.'/.'.$$event{Id};
} else { } else {
Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}"); Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}");
@ -255,6 +254,33 @@ sub LinkPath {
return $$event{LinkPath}; return $$event{LinkPath};
} # end sub 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 { sub GenerateVideo {
my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_; my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_;
@ -417,13 +443,13 @@ sub delete_files {
return; return;
} }
my $event_path = $event->RelativePath(); 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 ) { if ( $event_path ) {
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint ( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint ( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $deleted = 0; 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*$/ ); my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval { eval {
require Net::Amazon::S3; require Net::Amazon::S3;
@ -453,7 +479,7 @@ sub delete_files {
if ( $event->Scheme() eq 'Deep' ) { if ( $event->Scheme() eq 'Deep' ) {
my $link_path = $event->LinkPath(); 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 ) { if ( $link_path ) {
( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint ( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint
unlink($storage_path.'/'.$link_path) or Error( "Unable to unlink '$storage_path/$link_path': $!" ); unlink($storage_path.'/'.$link_path) or Error( "Unable to unlink '$storage_path/$link_path': $!" );
@ -564,6 +590,7 @@ sub MoveTo {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ ); my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval { eval {
require Net::Amazon::S3; require Net::Amazon::S3;
require File::Slurp;
my $s3 = Net::Amazon::S3->new( { my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id, aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret, aws_secret_access_key => $aws_secret,

View File

@ -47,10 +47,8 @@ our %EXPORT_TAGS = (
getCmdFormat getCmdFormat
runCommand runCommand
setFileOwner setFileOwner
getEventPath
createEventPath createEventPath
createEvent createEvent
deleteEventFiles
makePath makePath
jsonEncode jsonEncode
jsonDecode jsonDecode
@ -181,70 +179,14 @@ sub runCommand {
return( $output ); 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 { sub createEventPath {
#
# WARNING assumes running from events directory
#
my $event = shift; my $event = shift;
my $Storage = new ZoneMinder::Storage( $$event{Id} ); my $eventPath = $event->Path();
my $eventPath = $Storage->Path() . '/'.$event->{MonitorId}; $event->createPath();
$event->createIdFile();
$event->createLinkPath();
if ( $Config{ZM_USE_DEEP_STORAGE} ) { return $eventPath;
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 );
} }
use Data::Dumper; use Data::Dumper;
@ -481,53 +423,6 @@ sub updateEvent {
or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); 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 { sub makePath {
my $path = shift; my $path = shift;
my $root = shift; my $root = shift;

View File

@ -440,7 +440,7 @@ sub databaseLevel {
if ( defined($databaseLevel) ) { if ( defined($databaseLevel) ) {
$databaseLevel = $this->limit($databaseLevel); $databaseLevel = $this->limit($databaseLevel);
if ( $this->{databaseLevel} != $databaseLevel ) { if ( $this->{databaseLevel} != $databaseLevel ) {
if ( $databaseLevel > NOLOG and $this->{databaseLevel} <= NOLOG ) { if ( ( $databaseLevel > NOLOG ) and ( $this->{databaseLevel} <= NOLOG ) ) {
if ( !$this->{dbh} ) { if ( !$this->{dbh} ) {
$this->{dbh} = ZoneMinder::Database::zmDbConnect(); $this->{dbh} = ZoneMinder::Database::zmDbConnect();
} }
@ -527,6 +527,7 @@ sub logPrint {
my $this = shift; my $this = shift;
my $level = shift; my $level = shift;
my $string = shift; my $string = shift;
my ($caller, undef, $line) = @_ ? @_ : caller;
if ( $level <= $this->{effectiveLevel} ) { if ( $level <= $this->{effectiveLevel} ) {
$string =~ s/[\r\n]+$//g; $string =~ s/[\r\n]+$//g;
@ -537,12 +538,14 @@ sub logPrint {
my ($seconds, $microseconds) = gettimeofday(); my ($seconds, $microseconds) = gettimeofday();
if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) { if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) {
my $message = sprintf( my $message = sprintf(
'%s.%06d %s[%d].%s [%s]' '%s.%06d %s[%d].%s [%s:%d] [%s]'
, strftime('%x %H:%M:%S', localtime($seconds)) , strftime('%x %H:%M:%S', localtime($seconds))
, $microseconds , $microseconds
, $this->{id} , $this->{id}
, $$ , $$
, $codes{$level} , $codes{$level}
, $caller
, $line
, $string , $string
); );
if ( $this->{trace} ) { if ( $this->{trace} ) {
@ -557,11 +560,14 @@ sub logPrint {
if ( $level <= $this->{databaseLevel} ) { if ( $level <= $this->{databaseLevel} ) {
if ( ! ( $this->{dbh} and $this->{dbh}->ping() ) ) { if ( ! ( $this->{dbh} and $this->{dbh}->ping() ) ) {
$this->{sth} = undef; $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() ) ) { if ( ! ( $this->{dbh} = ZoneMinder::Database::zmDbConnect() ) ) {
#print(STDERR "Can't log to database: "); #print(STDERR "Can't log to database: ");
$this->{databaseLevel} = NOLOG;
return; 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, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
@ -660,39 +666,39 @@ sub Dump {
sub debug { sub debug {
my $log = shift; my $log = shift;
$log->logPrint(DEBUG, @_); $log->logPrint(DEBUG, @_, caller);
} }
sub Debug( @ ) { sub Debug( @ ) {
fetch()->logPrint(DEBUG, @_); fetch()->logPrint(DEBUG, @_, caller);
} }
sub Info( @ ) { sub Info( @ ) {
fetch()->logPrint(INFO, @_); fetch()->logPrint(INFO, @_, caller);
} }
sub info { sub info {
my $log = shift; my $log = shift;
$log->logPrint(INFO, @_); $log->logPrint(INFO, @_, caller);
} }
sub Warning( @ ) { sub Warning( @ ) {
fetch()->logPrint(WARNING, @_); fetch()->logPrint(WARNING, @_, caller);
} }
sub warn { sub warn {
my $log = shift; my $log = shift;
$log->logPrint(WARNING, @_); $log->logPrint(WARNING, @_, caller);
} }
sub Error( @ ) { sub Error( @ ) {
fetch()->logPrint(ERROR, @_); fetch()->logPrint(ERROR, @_, caller);
} }
sub error { sub error {
my $log = shift; my $log = shift;
$log->logPrint(ERROR, @_); $log->logPrint(ERROR, @_, caller);
} }
sub Fatal( @ ) { sub Fatal( @ ) {
fetch()->logPrint(FATAL, @_); fetch()->logPrint(FATAL, @_, caller);
if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) { if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) {
$SIG{TERM}(); $SIG{TERM}();
} }
@ -700,7 +706,7 @@ sub Fatal( @ ) {
} }
sub Panic( @ ) { sub Panic( @ ) {
fetch()->logPrint(PANIC, @_); fetch()->logPrint(PANIC, @_, caller);
confess($_[0]); confess($_[0]);
} }

View File

@ -69,7 +69,7 @@ sub find {
push @sql_values, $sql_filters{Name}; push @sql_values, $sql_filters{Name};
} }
if ( exists $sql_filters{ServerId} ) { if ( exists $sql_filters{ServerId} ) {
push @sql_filters, ' Id IN ( SELECT StorageId FROM Monitors WHERE ServerId=? )'; push @sql_filters, ' ServerId = ?';
push @sql_values, $sql_filters{ServerId}; push @sql_values, $sql_filters{ServerId};
} }
@ -88,6 +88,7 @@ sub find {
my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter ); my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter );
push @results, $filter; push @results, $filter;
} # end while } # end while
Debug("SQL: $sql returned " . @results . ' results');
return @results; return @results;
} }

View File

@ -67,6 +67,7 @@ my $level = 1;
my $monitor_id = 0; my $monitor_id = 0;
my $version; my $version;
my $force = 0; my $force = 0;
my $server_id = undef;
my $storage_id = undef; my $storage_id = undef;
logInit(); logInit();
@ -78,6 +79,7 @@ GetOptions(
level =>\$level, level =>\$level,
'monitor_id=i' =>\$monitor_id, 'monitor_id=i' =>\$monitor_id,
report =>\$report, report =>\$report,
'server_id=i' =>\$server_id,
'storage_id=i' =>\$storage_id, 'storage_id=i' =>\$storage_id,
version =>\$version version =>\$version
) or pod2usage(-exitstatus => -1); ) or pod2usage(-exitstatus => -1);
@ -181,13 +183,15 @@ MAIN: while( $loop ) {
Term(); Term();
} }
Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}"); Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}");
} elsif ( $Config{ZM_SERVER_ID} ) { } elsif ( $server_id ) {
@Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); @Storage_Areas = ZoneMinder::Storage->find( ServerId => $server_id );
if ( ! @Storage_Areas ) { if ( ! @Storage_Areas ) {
Error("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID}); Error("No Storage Area found with ServerId =" . $server_id);
Term(); 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 { } else {
@Storage_Areas = ZoneMinder::Storage->find(); @Storage_Areas = ZoneMinder::Storage->find();
Info("Auditing All Storage Areas"); Info("Auditing All Storage Areas");
@ -260,31 +264,37 @@ MAIN: while( $loop ) {
Error("Can't open directory '$$Storage{Path}/$day_dir': $!"); Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
next; next;
} }
my %event_ids_by_path;
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR ); my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
Debug("Have " . @event_links . ' event links'); Debug("Have " . @event_links . ' event links');
closedir(DIR); closedir(DIR);
my $count = 0; my $count = 0;
foreach my $event_link ( @event_links ) { 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"); Warning("Non-event link found $event_link in $day_dir, skipping");
next; next;
} }
Debug("Checking link $event_link"); Debug("Checking link $event_link");
( my $event = $event_link ) =~ s/^.*\.//;
#Event path is hour/minute/sec #Event path is hour/minute/sec
my $event_path = readlink($event_link); my $event_path = readlink($event_link);
if ( !($event_path and -e $event_path) ) { 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() ) { if ( confirm() ) {
( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint ( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint
unlink($event_link); unlink($event_link);
$cleaned = 1; $cleaned = 1;
} }
} else { } else {
$event_ids_by_path{$event_path} = $event_id;
Debug("Checking link $event_link points to $event_path "); Debug("Checking link $event_link points to $event_path ");
my $Event = $fs_events->{$event} = new ZoneMinder::Event(); my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event; $$Event{Id} = $event_id;
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_path); $$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_path);
$$Event{RelativePath} = join('/', $day_dir, $event_path); $$Event{RelativePath} = join('/', $day_dir, $event_path);
$$Event{Scheme} = 'Deep'; $$Event{Scheme} = 'Deep';
@ -303,15 +313,34 @@ MAIN: while( $loop ) {
my $event_id = undef; 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 ) { foreach my $mp4_file ( @mp4_files ) {
my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/; my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/;
if ( $id ) { if ( $id ) {
$event_id = $id; $event_id = $id;
Debug("Got event id from mp4 file $mp4_file => $event_id");
last; 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(); my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id; $$Event{Id} = $event_id;
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir); $$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir);
@ -320,7 +349,26 @@ MAIN: while( $loop ) {
$Event->MonitorId( $monitor_dir ); $Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() ); $Event->StorageId( $Storage->Id() );
$Event->DiskSpace( undef ); $Event->DiskSpace( undef );
if ( ! $event_ids_by_path{$event_dir} ) {
Warning("No event link found at ".$Event->LinkPath() ." for " . $Event->to_string());
}
} else { } 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"); aud_print("Deleting event directories with no event id information at $day_dir/$event_dir");
if ( confirm() ) { if ( confirm() ) {
my $command = "rm -rf $event_dir"; my $command = "rm -rf $event_dir";
@ -359,7 +407,7 @@ MAIN: while( $loop ) {
} }
if ( ! $$Storage{Scheme} ) { if ( ! $$Storage{Scheme} ) {
Debug("Storage Scheme not set on $$Storage{Name}"); Error("Storage Scheme not set on $$Storage{Name}");
if ( ! chdir( $monitor_dir ) ) { if ( ! chdir( $monitor_dir ) ) {
Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" ); Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" );
next; next;
@ -382,7 +430,7 @@ MAIN: while( $loop ) {
} # if USE_DEEP_STORAGE } # if USE_DEEP_STORAGE
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir\n" ); 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 } # end foreach monitor
if ( $cleaned ) { if ( $cleaned ) {
@ -473,6 +521,10 @@ MAIN: while( $loop ) {
next; next;
} }
Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); 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() ) { if ( ! $Event->StartTime() ) {
Info("Event $$Event{Id} has no start time. deleting it."); Info("Event $$Event{Id} has no start time. deleting it.");
if ( confirm() ) { 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; my $DIR;
Debug("delete_empty_directories $_[0]");
if ( !opendir($DIR, $_[0]) ) { if ( !opendir($DIR, $_[0]) ) {
Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" ); Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
return; 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; my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
if ( @dirs ) { if ( @dirs ) {
Debug("Have " . @dirs . " dirs");
foreach ( @dirs ) { foreach ( @dirs ) {
delete_empty_directories( $_[0].'/'.$_ ); delete_empty_directories( $_[0].'/'.$_ );
} }
#Reload, since we may now be empty #Reload, since we may now be empty
rewinddir $DIR; rewinddir $DIR;
@contents = map { $_ eq '.' or $_ eq '..' ? () : $_ } readdir( $DIR ); @contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR );
}
if ( ! @contents ) {
( my $dir ) = ( $_[0] =~ /^(.*)$/ );
unlink $dir;
} }
closedir($DIR); closedir($DIR);
if ( ! @contents ) {
( my $dir ) = ( $_[0] =~ /^(.*)$/ );
Debug("Unlinking $dir because it's empty");
if ( ! rmdir $dir ) {
Error("Unable to unlink $dir: $!");
}
}
} # end sub delete_empty_directories } # end sub delete_empty_directories
1; 1;

View File

@ -21,32 +21,6 @@
# #
# ========================================================================== # ==========================================================================
=head1 NAME
zmcontrol.pl - ZoneMinder control script
=head1 SYNOPSIS
zmcontrol.pl --id {monitor_id} --command={command} [various options]
=head1 DESCRIPTION
FIXME FIXME
=head1 OPTIONS
--autostop -
--xcoord [ arg ] - X-coord
--ycoord [ arg ] - Y-coord
--speed [ arg ] - Speed
--step [ arg ] -
--panspeed [ arg ] -
--panstep [ arg ] -
--tiltspeed [ arg ] -
--tiltstep [ arg ] -
--preset [ arg ] -
=cut
use strict; use strict;
@EXTRA_PERL_LIB@ @EXTRA_PERL_LIB@
@ -58,7 +32,7 @@ use Socket;
#use Data::Dumper; #use Data::Dumper;
use Module::Load::Conditional qw{can_load};; use Module::Load::Conditional qw{can_load};;
use constant MAX_CONNECT_DELAY => 10; use constant MAX_CONNECT_DELAY => 15;
use constant MAX_COMMAND_WAIT => 1800; use constant MAX_COMMAND_WAIT => 1800;
$| = 1; $| = 1;
@ -89,15 +63,14 @@ GetOptions(
'autostop' =>\$options{autostop}, 'autostop' =>\$options{autostop},
) or pod2usage(-exitstatus => -1); ) or pod2usage(-exitstatus => -1);
if ( !$id || !$options{command} ) if ( !$id || !$options{command} ) {
{
print( STDERR "Please give a valid monitor id and command\n" ); print( STDERR "Please give a valid monitor id and command\n" );
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
( $id ) = $id =~ /^(\w+)$/; ( $id ) = $id =~ /^(\w+)$/;
Debug( $arg_string ); Debug("zmcontrol: arg string: $arg_string");
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock'; my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
@ -106,33 +79,28 @@ socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
my $saddr = sockaddr_un($sock_file); my $saddr = sockaddr_un($sock_file);
my $server_up = connect(CLIENT, $saddr); my $server_up = connect(CLIENT, $saddr);
if ( !$server_up ) if ( !$server_up ) {
{
# The server isn't there # The server isn't there
my $monitor = zmDbGetMonitorAndControl($id); my $monitor = zmDbGetMonitorAndControl($id);
if ( !$monitor ) if ( !$monitor ) {
{
Fatal("Unable to load control data for monitor $id"); Fatal("Unable to load control data for monitor $id");
} }
my $protocol = $monitor->{Protocol}; my $protocol = $monitor->{Protocol};
if ( -x $protocol ) if ( -x $protocol ) {
{
# Protocol is actually a script! # Protocol is actually a script!
# Holdover from previous versions # Holdover from previous versions
my $command .= $protocol.' '.$arg_string; my $command .= $protocol.' '.$arg_string;
Debug( $command."\n" ); Debug($command);
my $output = qx($command); my $output = qx($command);
my $status = $? >> 8; my $status = $? >> 8;
if ( $status || logDebugging() ) if ( $status || logDebugging() ) {
{
chomp($output); chomp($output);
Debug( "Output: $output\n" ); Debug("Output: $output");
} }
if ( $status ) if ( $status ) {
{ Error("Command '$command' exited with status: $status");
Error( "Command '$command' exited with status: $status\n" );
exit($status); exit($status);
} }
exit(0); exit(0);
@ -142,26 +110,22 @@ if ( !$server_up )
close(CLIENT); close(CLIENT);
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) { if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
Fatal("Can't load ZoneMinder::Control::$protocol"); Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
} }
if ( my $cpid = fork() ) if ( my $cpid = fork() ) {
{
logReinit(); logReinit();
# Parent process just sleep and fall through # Parent process just sleep and fall through
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
or die("Can't open socket: $!"); or die("Can't open socket: $!");
my $attempts = 0; my $attempts = 0;
while (!connect( CLIENT, $saddr )) while ( !connect(CLIENT, $saddr) ) {
{
$attempts++; $attempts++;
Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY); Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY;
sleep(1); sleep(1);
} }
} } elsif ( defined($cpid) ) {
elsif ( defined($cpid) )
{
close(STDOUT); close(STDOUT);
close(STDERR); close(STDERR);
@ -192,17 +156,14 @@ if ( !$server_up )
my $win = $rin; my $win = $rin;
my $ein = $win; my $ein = $win;
my $timeout = MAX_COMMAND_WAIT; my $timeout = MAX_COMMAND_WAIT;
while( 1 ) while( 1 ) {
{
my $nfound = select(my $rout = $rin, undef, undef, $timeout); my $nfound = select(my $rout = $rin, undef, undef, $timeout);
if ( $nfound > 0 ) if ( $nfound > 0 ) {
{ if ( vec( $rout, fileno(SERVER), 1 ) ) {
if ( vec( $rout, fileno(SERVER), 1 ) )
{
my $paddr = accept(CLIENT, SERVER); my $paddr = accept(CLIENT, SERVER);
my $message = <CLIENT>; my $message = <CLIENT>;
next if ( !$message ); next if !$message;
my $params = jsonDecode($message); my $params = jsonDecode($message);
#Debug( Dumper( $params ) ); #Debug( Dumper( $params ) );
@ -213,41 +174,28 @@ if ( !$server_up )
last; last;
} }
$control->$command($params); $control->$command($params);
} else {
Fatal('Bogus descriptor');
} }
else } elsif ( $nfound < 0 ) {
{ if ( $! == EPIPE ) {
Fatal( "Bogus descriptor" );
}
}
elsif ( $nfound < 0 )
{
if ( $! == EPIPE )
{
Error("Can't select: $!"); Error("Can't select: $!");
} } else {
else
{
Fatal("Can't select: $!"); Fatal("Can't select: $!");
} }
} } else {
else
{
#print( "Select timed out\n" ); #print( "Select timed out\n" );
last; last;
} }
} } # end while forever
Info( "Control server $id/$protocol exiting at " Info("Control server $id/$protocol exiting");
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
);
unlink($sock_file); unlink($sock_file);
$control->close(); $control->close();
exit(0); exit(0);
} } else {
else
{
Fatal("Can't fork: $!"); Fatal("Can't fork: $!");
} }
} } # end if !server up
# The server is there, connect to it # The server is there, connect to it
#print( "Writing commands\n" ); #print( "Writing commands\n" );
@ -258,3 +206,33 @@ print( CLIENT $message );
shutdown(CLIENT, 1); shutdown(CLIENT, 1);
exit(0); exit(0);
1;
__END__
=head1 NAME
zmcontrol.pl - ZoneMinder control script
=head1 SYNOPSIS
zmcontrol.pl --id {monitor_id} --command={command} [various options]
=head1 DESCRIPTION
FIXME FIXME
=head1 OPTIONS
--autostop -
--xcoord [ arg ] - X-coord
--ycoord [ arg ] - Y-coord
--speed [ arg ] - Speed
--step [ arg ] -
--panspeed [ arg ] -
--panstep [ arg ] -
--tiltspeed [ arg ] -
--tiltstep [ arg ] -
--preset [ arg ] -
=cut

View File

@ -272,6 +272,8 @@ sub run {
."\n" ."\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 # Tell any existing processes to die, wait 1 second between TERM and KILL
killAll(1); killAll(1);

View File

@ -98,6 +98,11 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
logInit($filter_id?(id=>'zmfilter_'.$filter_id):()); logInit($filter_id?(id=>'zmfilter_'.$filter_id):());
sub HupHandler { sub HupHandler {
# This idea at this time is to just exit, freeing up the memory.
# zmfilter.pl will be respawned by zmdc.
TermHandler();
return;
Info('Received HUP, reloading'); Info('Received HUP, reloading');
ZoneMinder::Object::init_cache(); ZoneMinder::Object::init_cache();
&ZoneMinder::Logger::logHupHandler(); &ZoneMinder::Logger::logHupHandler();
@ -470,7 +475,7 @@ sub uploadArchFile {
} }
my $archFile = $event->{MonitorName}.'-'.$event->{Id}; my $archFile = $event->{MonitorName}.'-'.$event->{Id};
my $archImagePath = getEventPath($event) my $archImagePath = $event->Path()
.'/' .'/'
.( .(
( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) ( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
@ -708,14 +713,18 @@ sub substituteTags {
} }
} # end if $first_alarm_frame } # end if $first_alarm_frame
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) { if ( $attachments_ref ) {
if ( $text =~ s/%EV%//g ) { if ( $text =~ s/%EV%//g ) {
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); my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) { if ( !$format ) {
return undef; return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push( @$attachments_ref, { type=>"video/$format", path=>$path } );
} }
}
if ( $text =~ s/%EVM%//g ) { if ( $text =~ s/%EVM%//g ) {
my ( $format, $path ) = generateVideo($filter, $Event, 1); my ( $format, $path ) = generateVideo($filter, $Event, 1);
if ( !$format ) { if ( !$format ) {
@ -942,7 +951,7 @@ sub executeCommand {
my $filter = shift; my $filter = shift;
my $event = shift; my $event = shift;
my $event_path = getEventPath($event); my $event_path = $event->Path();
my $command = $filter->{AutoExecuteCmd}; my $command = $filter->{AutoExecuteCmd};
$command .= " $event_path"; $command .= " $event_path";

View File

@ -53,7 +53,6 @@ use constant START_DELAY => 30; # To give everything else time to start
@EXTRA_PERL_LIB@ @EXTRA_PERL_LIB@
use ZoneMinder; use ZoneMinder;
use ZoneMinder::Storage;
use POSIX; use POSIX;
use DBI; use DBI;
use autouse 'Data::Dumper'=>qw(Dumper); use autouse 'Data::Dumper'=>qw(Dumper);
@ -72,15 +71,13 @@ sleep( START_DELAY );
my $dbh = zmDbConnect(); 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( 1 ) {
while ( ! ( $dbh and $dbh->ping() ) ) { while ( ! ( $dbh and $dbh->ping() ) ) {
Info("Reconnecting to db"); 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()); $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr());

View File

@ -33,6 +33,7 @@ use LWP::UserAgent;
use Sys::MemInfo qw(totalmem); use Sys::MemInfo qw(totalmem);
use Sys::CPU qw(cpu_count); use Sys::CPU qw(cpu_count);
use POSIX qw(strftime uname); use POSIX qw(strftime uname);
use JSON::MaybeXS;
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
@ -87,7 +88,7 @@ while( 1 ) {
# We should keep *BSD systems in mind when calling system commands # We should keep *BSD systems in mind when calling system commands
my %telemetry; my %telemetry;
$telemetry{uuid} = getUUID($dbh); $telemetry{uuid} = getUUID($dbh);
$telemetry{ip} = getIP(); ($telemetry{city}, $telemetry{region}, $telemetry{country}, $telemetry{latitude}, $telemetry{longitude}) = getGeo();
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() ); $telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
$telemetry{monitor_count} = countQuery($dbh,'Monitors'); $telemetry{monitor_count} = countQuery($dbh,'Monitors');
$telemetry{event_count} = countQuery($dbh,'Events'); $telemetry{event_count} = countQuery($dbh,'Events');
@ -203,22 +204,25 @@ sub getUUID {
return $uuid; return $uuid;
} }
# Retrieves the local server's external IP address # Retrieve this server's general location information from a GeoIP database
sub getIP { sub getGeo {
my $ipaddr = '0.0.0.0'; my $unknown = 'Unknown';
my $endpoint = 'https://ipinfo.io/geo';
my $ua = LWP::UserAgent->new; my $ua = LWP::UserAgent->new;
my $server_endpoint = 'https://wiki.zoneminder.com/ip.php'; my $req = HTTP::Request->new(GET => $endpoint);
my $req = HTTP::Request->new(GET => $server_endpoint);
my $resp = $ua->request($req); my $resp = $ua->request($req);
my $resp_msg = $resp->decoded_content;
my $resp_code = $resp->code;
if ($resp->is_success) { if ($resp->is_success) {
$ipaddr = $resp->decoded_content; my $content = decode_json( $resp_msg );
(my $latitude, my $longitude) = split /,/, $content->{loc};
return ($content->{city}, $content->{region}, $content->{country}, $latitude, $longitude);
} else {
Warning("Geoip data retrieval returned HTTP POST error code: $resp_code");
Debug("Geoip data retrieval failure response message: $resp_msg");
return ($unknown, $unknown, $unknown, $unknown);
} }
Debug("Found external ip address of: $ipaddr");
return $ipaddr;
} }
# As the name implies, just your average mysql count query # As the name implies, just your average mysql count query

View File

@ -922,6 +922,11 @@ if ( $version ) {
zmDbDisconnect(); zmDbDisconnect();
die( "Can't find upgrade from version '$version'" ); 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" ); print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
} }
zmDbDisconnect(); zmDbDisconnect();

View File

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

View File

@ -33,8 +33,7 @@ class Camera;
// Abstract base class for cameras. This is intended just to express // Abstract base class for cameras. This is intended just to express
// common attributes // common attributes
// //
class Camera class Camera {
{
protected: protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
@ -55,42 +54,41 @@ protected:
bool record_audio; bool record_audio;
unsigned int bytes; unsigned int bytes;
public: 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 ); 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(); virtual ~Camera();
unsigned int getId() const { return( monitor_id ); } unsigned int getId() const { return monitor_id; }
Monitor *getMonitor(); Monitor *getMonitor();
void setMonitor( Monitor *p_monitor ); void setMonitor( Monitor *p_monitor );
SourceType Type() const { return( type ); } SourceType Type() const { return type; }
bool IsLocal() const { return( type == LOCAL_SRC ); } bool IsLocal() const { return type == LOCAL_SRC; }
bool IsRemote() const { return( type == REMOTE_SRC ); } bool IsRemote() const { return type == REMOTE_SRC; }
bool IsFile() const { return( type == FILE_SRC ); } bool IsFile() const { return type == FILE_SRC; }
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } bool IsFfmpeg() const { return type == FFMPEG_SRC; }
bool IsLibvlc() const { return( type == LIBVLC_SRC ); } bool IsLibvlc() const { return type == LIBVLC_SRC; }
bool IscURL() const { return( type == CURL_SRC ); } bool IscURL() const { return type == CURL_SRC; }
unsigned int Width() const { return( width ); } unsigned int Width() const { return width; }
unsigned int Height() const { return( height ); } unsigned int Height() const { return height; }
unsigned int Colours() const { return( colours ); } unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return( subpixelorder ); } unsigned int SubpixelOrder() const { return subpixelorder; }
unsigned int Pixels() const { return( pixels ); } unsigned int Pixels() const { return pixels; }
unsigned int ImageSize() const { return( imagesize ); } unsigned int ImageSize() const { return imagesize; }
unsigned int Bytes() const { return bytes; }; unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } virtual int Brightness( int/*p_brightness*/=-1 ) { return -1; }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } virtual int Hue( int/*p_hue*/=-1 ) { return -1; }
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } virtual int Colour( int/*p_colour*/=-1 ) { return -1; }
virtual int Contrast( int/*p_contrast*/=-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 { bool SupportsNativeVideo() const {
return (type == FFMPEG_SRC); return (type == FFMPEG_SRC);
//return (type == FFMPEG_SRC )||(type == REMOTE_SRC); //return (type == FFMPEG_SRC )||(type == REMOTE_SRC);
} }
virtual int PrimeCapture() { return( 0 ); } virtual int PrimeCapture() { return 0; }
virtual int PreCapture() = 0; virtual int PreCapture() = 0;
virtual int Capture(Image &image) = 0; virtual int Capture(Image &image) = 0;
virtual int PostCapture() = 0; virtual int PostCapture() = 0;

View File

@ -123,9 +123,9 @@ void process_configfile( char* configFile) {
if ( *line_ptr == '\0' || *line_ptr == '#' ) if ( *line_ptr == '\0' || *line_ptr == '#' )
continue; continue;
// Remove trailing white space // Remove trailing white space and trailing quotes
char *temp_ptr = line_ptr+strlen(line_ptr)-1; char *temp_ptr = line_ptr+strlen(line_ptr)-1;
while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) { while ( *temp_ptr == ' ' || *temp_ptr == '\t' || *temp_ptr == '\'' || *temp_ptr == '\"') {
*temp_ptr-- = '\0'; *temp_ptr-- = '\0';
temp_ptr--; temp_ptr--;
} }
@ -147,8 +147,9 @@ void process_configfile( char* configFile) {
temp_ptr--; temp_ptr--;
} while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); } while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
// Remove leading white space from the value part // Remove leading white space and leading quotes from the value part
white_len = strspn( val_ptr, " \t" ); white_len = strspn( val_ptr, " \t" );
white_len += strspn( val_ptr, "\'\"" );
val_ptr += white_len; val_ptr += white_len;
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )

View File

@ -120,9 +120,15 @@ Event::Event(
char id_file[PATH_MAX]; char id_file[PATH_MAX];
if ( storage->Scheme() == Storage::DEEP ) {
char *path_ptr = path; char *path_ptr = path;
path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id()); 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 ) {
int dt_parts[6]; int dt_parts[6];
dt_parts[0] = stime->tm_year-100; dt_parts[0] = stime->tm_year-100;
@ -141,10 +147,9 @@ Event::Event(
errno = 0; errno = 0;
if ( mkdir(path, 0755) ) { if ( mkdir(path, 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area. // 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)); Error("Can't mkdir %s: %s", path, strerror(errno));
} }
}
if ( i == 2 ) if ( i == 2 )
strncpy(date_path, path, sizeof(date_path)); strncpy(date_path, path, sizeof(date_path));
else if ( i >= 3 ) else if ( i >= 3 )
@ -155,29 +160,25 @@ Event::Event(
if ( symlink(time_path, id_file) < 0 ) if ( symlink(time_path, id_file) < 0 )
Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) { } else if ( storage->Scheme() == Storage::MEDIUM ) {
char *path_ptr = path;
path_ptr += snprintf( path_ptr += snprintf(
path_ptr, sizeof(path), "%s/%d/%04d-%02d-%02d", path_ptr, sizeof(path), "/%04d-%02d-%02d",
storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
); );
if ( mkdir(path, 0755) ) { 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)); Error("Can't mkdir %s: %s", path, strerror(errno));
} }
path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id); path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id);
if ( mkdir(path, 0755) ) { 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)); Error("Can't mkdir %s: %s", path, strerror(errno));
} }
} else { } 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 ( mkdir(path, 0755) ) {
if ( errno != EEXIST ) { if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno)); Error("Can't mkdir %s: %s", path, strerror(errno));
} }
}
// Create empty id tag file // Create empty id tag file
snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, path, id); snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, path, id);
@ -540,7 +541,7 @@ Debug(3, "Writing video");
if ( score < 0 ) if ( score < 0 )
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 ) { if ( db_frame ) {
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] ); Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
@ -594,11 +595,12 @@ Debug(3, "Writing video");
max_score = score; max_score = score;
if ( alarm_image ) { if ( alarm_image ) {
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
Debug(1, "Writing analysis frame %d", frames);
if ( monitor->GetOptSaveJPEGs() & 2 ) { if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage(alarm_image, timestamp, event_file, true); snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
Debug(1, "Writing analysis frame %d", frames);
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
Error("Failed to write analysis frame image");
}
} }
} }
} }

View File

@ -649,10 +649,8 @@ void Image::Assign( const Image &image ) {
(*fptr_imgbufcpy)(buffer, image.buffer, size); (*fptr_imgbufcpy)(buffer, image.buffer, size);
} }
Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) {
{ if ( colours != ZM_COLOUR_GRAY8 ) {
if ( colours != ZM_COLOUR_GRAY8 )
{
Panic("Attempt to highlight image edges when colours = %d", colours); Panic("Attempt to highlight image edges when colours = %d", colours);
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
#endif #endif
bool Logger::smInitialised = false; bool Logger::smInitialised = false;
Logger *Logger::smInstance = 0; Logger *Logger::smInstance = NULL;
Logger::StringMap Logger::smCodes; Logger::StringMap Logger::smCodes;
Logger::IntMap Logger::smSyslogPriorities; Logger::IntMap Logger::smSyslogPriorities;
@ -57,9 +57,9 @@ static void subtractTime( struct timeval * const tp1, struct timeval * const tp2
void Logger::usrHandler( int sig ) { void Logger::usrHandler( int sig ) {
Logger *logger = fetch(); Logger *logger = fetch();
if ( sig == SIGUSR1 ) if ( sig == SIGUSR1 ) {
logger->level(logger->level()+1); logger->level(logger->level()+1);
else if ( sig == SIGUSR2 ) } else if ( sig == SIGUSR2 )
logger->level(logger->level()-1); logger->level(logger->level()-1);
Info("Logger - Level changed to %d", logger->level()); Info("Logger - Level changed to %d", logger->level());
} }
@ -133,11 +133,11 @@ void Logger::initialise(const std::string &id, const Options &options) {
std::string tempLogFile; std::string tempLogFile;
if ( (envPtr = getTargettedEnv("LOG_FILE")) ) if ( (envPtr = getTargettedEnv("LOG_FILE")) ) {
tempLogFile = envPtr; tempLogFile = envPtr;
else if ( options.mLogFile.size() ) } else if ( options.mLogFile.size() ) {
tempLogFile = options.mLogFile; tempLogFile = options.mLogFile;
else { } else {
if ( options.mLogPath.size() ) { if ( options.mLogPath.size() ) {
mLogPath = options.mLogPath; mLogPath = options.mLogPath;
} }
@ -335,6 +335,10 @@ Logger::Level Logger::level(Logger::Level level) {
mEffectiveLevel = mSyslogLevel; mEffectiveLevel = mSyslogLevel;
if ( mEffectiveLevel > mLevel) if ( mEffectiveLevel > mLevel)
mEffectiveLevel = mLevel; mEffectiveLevel = mLevel;
// DEBUG levels should flush
if ( mLevel > INFO )
mFlush = true;
} }
return mLevel; return mLevel;
} }
@ -540,7 +544,13 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
if ( ! db_mutex.trylock() ) { if ( ! db_mutex.trylock() ) {
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) ); mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line ); snprintf(sql, sizeof(sql),
"INSERT INTO Logs "
"( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line )"
" VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line
);
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Level tempDatabaseLevel = mDatabaseLevel; Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel(NOLOG); databaseLevel(NOLOG);

View File

@ -423,16 +423,10 @@ Monitor::Monitor(
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
if ( purpose == CAPTURE ) { if ( purpose == CAPTURE ) {
struct stat statbuf;
if ( stat(monitor_dir, &statbuf) ) {
if ( errno == ENOENT || errno == ENOTDIR ) {
if ( mkdir(monitor_dir, 0755) ) { if ( mkdir(monitor_dir, 0755) ) {
if ( errno != EEXIST ) {
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); 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 ( ! this->connect() ) { if ( ! this->connect() ) {
@ -609,7 +603,8 @@ bool Monitor::connect() {
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval; next_buffer.timestamp = new struct timeval;
} }
if ( ( purpose == ANALYSIS ) && analysis_fps ) { if ( purpose == ANALYSIS ) {
if ( analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count // Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains // if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created // alarmed images that must be discarded when event is created
@ -620,6 +615,7 @@ bool Monitor::connect() {
*pre_event_buffer[i].timestamp = {0,0}; *pre_event_buffer[i].timestamp = {0,0};
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
} }
}
timestamps = new struct timeval *[pre_event_count]; timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count]; images = new Image *[pre_event_count];
@ -1577,8 +1573,8 @@ Error("Creating new event when one exists");
else else
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; 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", 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, image_buffer_count); pre_index, index, image_buffer_count, pre_event_count);
// Seek forward the next filled slot in to the buffer (oldest data) // Seek forward the next filled slot in to the buffer (oldest data)
// from the current position // from the current position
@ -1623,7 +1619,6 @@ Error("Creating new event when one exists");
pre_index = (pre_index + 1)%image_buffer_count; pre_index = (pre_index + 1)%image_buffer_count;
} }
} }
event->AddFrames( pre_event_images, images, timestamps ); event->AddFrames( pre_event_images, images, timestamps );
} }
if ( alarm_frame_count ) { if ( alarm_frame_count ) {
@ -1736,6 +1731,7 @@ Error("Creating new event when one exists");
} }
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
last_section_mod = 0; last_section_mod = 0;
trigger_data->trigger_state = TRIGGER_CANCEL;
} // end if ( trigger_data->trigger_state != TRIGGER_OFF ) } // end if ( trigger_data->trigger_state != TRIGGER_OFF )
if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) { if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) {
@ -2388,7 +2384,9 @@ int Monitor::Capture() {
} }
if ( captureResult < 0 ) { 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 // Unable to capture image for temporary reason
// Fake a signal loss image // Fake a signal loss image
Rgb signalcolor; Rgb signalcolor;
@ -2474,9 +2472,7 @@ 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( "%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); Info("%s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", name, image_count, new_fps, new_capture_bandwidth);
last_fps_time = now; last_fps_time = now;
if ( new_fps != fps ) {
fps = new_fps; fps = new_fps;
db_mutex.lock(); db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
@ -2486,7 +2482,7 @@ int Monitor::Capture() {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
} }
db_mutex.unlock(); db_mutex.unlock();
} // end if new_fps != fps Debug(4,sql);
} // end if time has changed since last update } // end if time has changed since last update
} // end if it might be time to report the fps } // end if it might be time to report the fps
} // end if captureResult } // end if captureResult

View File

@ -290,6 +290,8 @@ protected:
bool last_signal; bool last_signal;
bool last_signal;
double fps; double fps;
unsigned int last_camera_bytes; unsigned int last_camera_bytes;

View File

@ -482,7 +482,7 @@ void MonitorStream::runStream() {
swap_path = staticConfig.PATH_SWAP; swap_path = staticConfig.PATH_SWAP;
Debug( 3, "Checking swap path folder: %s", swap_path.c_str() ); 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()); swap_path += stringtf("/zmswap-m%d", monitor->Id());
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str()); Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
@ -633,7 +633,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
// Send the next frame // Send the next frame
Monitor::Snapshot *snap = &monitor->image_buffer[index]; Monitor::Snapshot *snap = &monitor->image_buffer[index];
//Debug(2, "sending Frame."); Debug(2, "sending Frame.");
if ( !sendFrame(snap->image, snap->timestamp) ) { if ( !sendFrame(snap->image, snap->timestamp) ) {
Debug(2, "sendFrame failed, quiting."); Debug(2, "sendFrame failed, quiting.");
zm_terminate = true; zm_terminate = true;
@ -687,7 +687,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
} // end if buffered playback } // end if buffered playback
frame_count++; frame_count++;
} else { } 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 ) } // 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))); 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() { void StreamBase::openComms() {
if ( connkey > 0 ) { 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) ) { if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated."); Warning("Socket lock path was truncated.");
} }
@ -292,7 +305,13 @@ void StreamBase::openComms() {
Debug(1, "Have socket %d", sd); 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) ) { if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated."); Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1; length = sizeof(loc_sock_path)-1;

View File

@ -178,7 +178,6 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
} }
Monitor::Orientation orientation = monitor->getOrientation(); Monitor::Orientation orientation = monitor->getOrientation();
Debug(3, "Have orientation");
if (orientation) { if (orientation) {
if (orientation == Monitor::ROTATE_0) { if (orientation == Monitor::ROTATE_0) {
} else if (orientation == Monitor::ROTATE_90) { } else if (orientation == Monitor::ROTATE_90) {
@ -336,14 +335,14 @@ bool VideoStore::open() {
} else if (av_dict_count(opts) != 0) { } else if (av_dict_count(opts) != 0) {
Warning("some options not set\n"); Warning("some options not set\n");
} }
if (opts) av_dict_free(&opts);
if (ret < 0) { if (ret < 0) {
Error("Error occurred when writing out file header to %s: %s\n", Error("Error occurred when writing out file header to %s: %s\n",
filename, av_make_error_string(ret).c_str()); filename, av_make_error_string(ret).c_str());
return false; return false;
} }
if (opts) av_dict_free(&opts);
return true; return true;
} } // end VideoStore::open()
VideoStore::~VideoStore() { VideoStore::~VideoStore() {
@ -353,6 +352,9 @@ VideoStore::~VideoStore() {
// The codec queues data. We need to send a flush command and out // The codec queues data. We need to send a flush command and out
// whatever we get. Failures are not fatal. // whatever we get. Failures are not fatal.
AVPacket pkt; AVPacket pkt;
// Without these we seg fault I don't know why.
pkt.data = NULL;
pkt.size = 0;
av_init_packet(&pkt); av_init_packet(&pkt);
while (1) { while (1) {
@ -402,8 +404,10 @@ VideoStore::~VideoStore() {
} // end if audio_out_codec } // end if audio_out_codec
// Flush Queues // Flush Queues
Debug(1,"Flushing interleaved queues");
av_interleaved_write_frame(oc, NULL); av_interleaved_write_frame(oc, NULL);
Debug(1,"Writing trailer");
/* Write the trailer before close */ /* Write the trailer before close */
if (int rc = av_write_trailer(oc)) { if (int rc = av_write_trailer(oc)) {
Error("Error writing trailer %s", av_err2str(rc)); Error("Error writing trailer %s", av_err2str(rc));
@ -411,7 +415,7 @@ VideoStore::~VideoStore() {
Debug(3, "Sucess Writing trailer"); Debug(3, "Sucess Writing trailer");
} }
// WHen will be not using a file ? // When will we not be using a file ?
if ( !(out_format->flags & AVFMT_NOFILE) ) { if ( !(out_format->flags & AVFMT_NOFILE) ) {
/* Close the out file. */ /* Close the out file. */
Debug(2, "Closing"); Debug(2, "Closing");
@ -507,6 +511,7 @@ bool VideoStore::setup_resampler() {
} }
Debug(2, "Have audio out codec"); 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 = audio_out_stream->codec;
audio_out_ctx = avcodec_alloc_context3(audio_out_codec); audio_out_ctx = avcodec_alloc_context3(audio_out_codec);
@ -517,6 +522,12 @@ bool VideoStore::setup_resampler() {
} }
Debug(2, "Have audio_out_ctx"); 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 */ /* put sample parameters */
audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; audio_out_ctx->bit_rate = audio_in_ctx->bit_rate;
@ -559,8 +570,6 @@ bool VideoStore::setup_resampler() {
audio_out_ctx->time_base = audio_out_ctx->time_base =
(AVRational){1, audio_out_ctx->sample_rate}; (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) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
ret = avcodec_parameters_from_context(audio_out_stream->codecpar, 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 ) if ( pixel_diff_count && alarm_pixels )
pixel_diff = 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 ( alarm_pixels ) {
if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_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; return false;
} }
if (max_alarm_pixels != 0)
score = (100*alarm_pixels)/max_alarm_pixels;
else
score = (100*alarm_pixels)/polygon.Area(); score = (100*alarm_pixels)/polygon.Area();
if ( score < 1 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ 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); Debug(5, "Current score is %d", score);
@ -312,7 +318,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
if ( config.record_diag_images ) if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path); 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 ( alarm_filter_pixels ) {
if ( min_filter_pixels && (alarm_filter_pixels < min_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; 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 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ 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); Debug(5, "Current score is %d", score);
@ -438,7 +449,8 @@ bool Zone::CheckAlarms(const Image *delta_image) {
alarm_blobs--; 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 // Clear out the old blob
bss->tag = 0; bss->tag = 0;
@ -476,7 +488,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
BlobStats *bs = &blob_stats[i]; BlobStats *bs = &blob_stats[i];
// See if we can recycle one first, only if it's at least two rows up // 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 ( 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 ) { if ( config.create_analysis_images || config.record_diag_images ) {
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) { for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) {
spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); spdiff = diff_buff + ((diff_width * sy) + bs->lo_x);
@ -590,7 +606,11 @@ bool Zone::CheckAlarms(const Image *delta_image) {
return false; 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 ) if ( score < 1 )
score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ 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); Debug(5, "Current score is %d", score);

View File

@ -181,5 +181,5 @@ int main( int argc, char *argv[] ) {
Image::Deinitialise(); Image::Deinitialise();
logTerm(); logTerm();
zmDbClose(); 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 ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) {
if ( monitors[i]->PreCapture() < 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(); monitors[i]->Close();
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->Capture() < 0 ) { 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(); monitors[i]->Close();
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->PostCapture() < 0 ) { 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(); monitors[i]->Close();
result = -1; result = -1;
break; break;

View File

@ -93,7 +93,8 @@ if [ "$RELEASE" != "" ]; then
else else
GITHUB_FORK="ZoneMinder"; GITHUB_FORK="ZoneMinder";
fi fi
BRANCH="release-$RELEASE" # We use a tag instead of a branch atm.
BRANCH=$RELEASE
else else
if [ "$GITHUB_FORK" == "" ]; then if [ "$GITHUB_FORK" == "" ]; then
echo "Defaulting to ZoneMinder upstream git" echo "Defaulting to ZoneMinder upstream git"
@ -188,6 +189,12 @@ if [ "$URGENCY" = "" ]; then
URGENCY="medium" URGENCY="medium"
fi; fi;
rm -rf .git
rm .gitignore
cd ../
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
cd $DIRECTORY.orig
if [ "$SNAPSHOT" == "stable" ]; then if [ "$SNAPSHOT" == "stable" ]; then
cat <<EOF > debian/changelog cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
@ -221,12 +228,6 @@ zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
EOF EOF
fi; fi;
rm -rf .git
rm .gitignore
cd ../
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
cd $DIRECTORY.orig
if [ $TYPE == "binary" ]; then if [ $TYPE == "binary" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file # Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs sudo apt-get install devscripts equivs
@ -287,7 +288,8 @@ else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes"; SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
PPA=""; PPA="";
if [ "$RELEASE" != "" ]; then if [ "$RELEASE" != "" ]; then
PPA="ppa:iconnor/zoneminder"; IFS='.' read -r -a VERSION <<< "$RELEASE"
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
else else
if [ "$BRANCH" == "" ]; then if [ "$BRANCH" == "" ]; then
PPA="ppa:iconnor/zoneminder-master"; PPA="ppa:iconnor/zoneminder-master";

View File

@ -154,7 +154,7 @@ movecrud () {
echo "Unpacking CakePHP-Enum-Behavior plugin..." echo "Unpacking CakePHP-Enum-Behavior plugin..."
tar -xzf build/cakephp-enum-behavior-${CEBVER}.tar.gz tar -xzf build/cakephp-enum-behavior-${CEBVER}.tar.gz
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior 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 fi
} }

View File

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

View File

@ -1 +1 @@
1.31.44 1.32.0

View File

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

View File

@ -33,7 +33,7 @@ switch ( $_REQUEST['task'] ) {
if ( !canView('System') ) if ( !canView('System') )
ajaxError('Insufficient permissions to view log entries'); ajaxError('Insufficient permissions to view log entries');
$servers = Server::find_all(); $servers = Server::find();
$servers_by_Id = array(); $servers_by_Id = array();
# There is probably a better way to do this. # There is probably a better way to do this.
foreach ( $servers as $server ) { foreach ( $servers as $server ) {
@ -59,7 +59,7 @@ switch ( $_REQUEST['task'] ) {
$sortField = $_REQUEST['sortField']; $sortField = $_REQUEST['sortField'];
} }
} }
$sortOrder = (isset($_REQUEST['sortOrder']) and $_REQUEST['sortOrder']) == 'asc' ? 'asc':'desc'; $sortOrder = (isset($_REQUEST['sortOrder']) and ($_REQUEST['sortOrder'] == 'asc')) ? 'asc' : 'desc';
$filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array(); $filter = isset($_REQUEST['filter']) ? $_REQUEST['filter'] : array();
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total'); $total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
@ -93,7 +93,10 @@ switch ( $_REQUEST['task'] ) {
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit; $sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
$logs = array(); $logs = array();
foreach ( dbFetchAll($sql, NULL, $values) as $log ) { foreach ( dbFetchAll($sql, NULL, $values) as $log ) {
$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
#Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']);
#$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']);
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']); $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
foreach( $filterFields as $field ) { foreach( $filterFields as $field ) {
@ -109,7 +112,7 @@ switch ( $_REQUEST['task'] ) {
} else if ( $field == 'ServerId' ) { } else if ( $field == 'ServerId' ) {
$options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : ''; $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : '';
} else if ( isset($log[$field]) ) { } else if ( isset($log[$field]) ) {
$options[$field][$log[$field]] = $log[$field]; $options[$field][$log[$field]] = $value;
} }
} }
$logs[] = $log; $logs[] = $log;
@ -122,7 +125,7 @@ switch ( $_REQUEST['task'] ) {
'available' => isset($available) ? $available : $total, 'available' => isset($available) ? $available : $total,
'logs' => $logs, 'logs' => $logs,
'state' => logState(), 'state' => logState(),
'options' => $options 'options' => $options,
) ); ) );
break; break;
} }
@ -150,7 +153,7 @@ switch ( $_REQUEST['task'] ) {
} }
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc';
$servers = Server::find_all(); $servers = Server::find();
$servers_by_Id = array(); $servers_by_Id = array();
# There is probably a better way to do this. # There is probably a better way to do this.
foreach ( $servers as $server ) { foreach ( $servers as $server ) {
@ -347,7 +350,7 @@ switch ( $_REQUEST['task'] ) {
</logexport>' ); </logexport>' );
break; break;
} }
$exportExt = "xml"; $exportExt = 'xml';
break; break;
} }
fclose( $exportFP ); fclose( $exportFP );
@ -363,10 +366,10 @@ switch ( $_REQUEST['task'] ) {
ajaxError('Insufficient permissions to download logs'); ajaxError('Insufficient permissions to download logs');
if ( empty($_REQUEST['key']) ) if ( empty($_REQUEST['key']) )
Fatal( "No log export key given" ); Fatal('No log export key given');
$exportKey = $_REQUEST['key']; $exportKey = $_REQUEST['key'];
if ( empty($_REQUEST['format']) ) if ( empty($_REQUEST['format']) )
Fatal( "No log export format given" ); Fatal('No log export format given');
$format = $_REQUEST['format']; $format = $_REQUEST['format'];
switch( $format ) { switch( $format ) {
@ -389,15 +392,15 @@ switch ( $_REQUEST['task'] ) {
$exportFile = "zm-log.$exportExt"; $exportFile = "zm-log.$exportExt";
$exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt";
header( "Pragma: public" ); header('Pragma: public');
header( "Expires: 0" ); header('Expires: 0');
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" ); header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header( "Cache-Control: private", false ); // required by certain browsers header('Cache-Control: private', false ); // required by certain browsers
header( "Content-Description: File Transfer" ); header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename="'.$exportFile.'"' ); header('Content-Disposition: attachment; filename="'.$exportFile.'"' );
header( "Content-Transfer-Encoding: binary" ); header('Content-Transfer-Encoding: binary');
header( "Content-Type: application/force-download" ); header('Content-Type: application/force-download');
header( "Content-Length: ".filesize($exportPath) ); header('Content-Length: '.filesize($exportPath));
readfile($exportPath); readfile($exportPath);
exit(0); exit(0);
break; break;

View File

@ -60,30 +60,47 @@ class AppController extends Controller {
// for role and deny API access in future // for role and deny API access in future
// Also checking to do this only if ZM_OPT_USE_AUTH is on // Also checking to do this only if ZM_OPT_USE_AUTH is on
public function beforeFilter() { public function beforeFilter() {
$this->loadModel('Config'); if ( ! ZM_OPT_USE_API ) {
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_API'));
$config = $this->Config->find('first', $options);
$zmOptApi = $config['Config']['Value'];
if ( $zmOptApi != '1' ) {
throw new UnauthorizedException(__('API Disabled')); throw new UnauthorizedException(__('API Disabled'));
return; return;
} }
# For use throughout the app. If not logged in, this will be null.
global $user;
$user = $this->Session->read('user');
if ( ZM_OPT_USE_AUTH ) {
require_once '../../../includes/auth.php';
$mUser = $this->request->query('user') ? $this->request->query('user') : $this->request->data('user');
$mPassword = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');
$mAuth = $this->request->query('auth') ? $this->request->query('auth') : $this->request->data('auth');
if ( $mUser and $mPassword ) {
$user = userLogin($mUser, $mPassword);
if ( !$user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
} else if ( $mAuth ) {
$user = getAuthUser($mAuth);
if ( !$user ) {
throw new UnauthorizedException(__('Invalid Auth Key'));
return;
}
}
// We need to reject methods that are not authenticated // We need to reject methods that are not authenticated
// besides login and logout // besides login and logout
if ( if ( strcasecmp($this->params->action, 'logout') ) {
strcasecmp($this->params->action, 'login') if ( !( $user and $user['Username'] ) ) {
&&
strcasecmp($this->params->action,"logout")
) {
if ( !$this->Session->read('user.Username') ) {
throw new UnauthorizedException(__('Not Authenticated')); throw new UnauthorizedException(__('Not Authenticated'));
return; return;
} else if ( !$this->Session->read('user.Enabled') ) { } else if ( !( $user and $user['Enabled'] ) ) {
throw new UnauthorizedException(__('User is not enabled')); throw new UnauthorizedException(__('User is not enabled'));
return; return;
} }
} } # end if ! login or logout
} # end if ZM_OPT_AUTH
} # end function beforeFilter() } # end function beforeFilter()
} }

View File

@ -1,5 +1,6 @@
<?php <?php
App::uses('AppController', 'Controller'); App::uses('AppController', 'Controller');
/** /**
* Events Controller * Events Controller
* *
@ -16,8 +17,9 @@ class EventsController extends AppController {
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('eventPermission'); global $user;
if ( $canView == 'None' ) { $canView = (!$user) || ($user['Events'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
@ -32,9 +34,10 @@ class EventsController extends AppController {
public function index() { public function index() {
$this->Event->recursive = -1; $this->Event->recursive = -1;
$allowedMonitors = preg_split('@,@', $this->Session->Read('allowedMonitors'), NULL, PREG_SPLIT_NO_EMPTY); global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( !empty($allowedMonitors) ) { if ( $allowedMonitors ) {
$mon_options = array('Event.MonitorId' => $allowedMonitors); $mon_options = array('Event.MonitorId' => $allowedMonitors);
} else { } else {
$mon_options = ''; $mon_options = '';
@ -91,7 +94,7 @@ class EventsController extends AppController {
} }
$this->set(compact('events')); $this->set(compact('events'));
} } // end public function index()
/** /**
* view method * view method
@ -108,9 +111,10 @@ class EventsController extends AppController {
throw new NotFoundException(__('Invalid event')); throw new NotFoundException(__('Invalid event'));
} }
$allowedMonitors = preg_split('@,@', $this->Session->Read('allowedMonitors'), NULL, PREG_SPLIT_NO_EMPTY); global $user;
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( !empty($allowedMonitors) ) { if ( $allowedMonitors ) {
$mon_options = array('Event.MonitorId' => $allowedMonitors); $mon_options = array('Event.MonitorId' => $allowedMonitors);
} else { } else {
$mon_options = ''; $mon_options = '';
@ -149,7 +153,9 @@ class EventsController extends AppController {
*/ */
public function add() { public function add() {
if ( $this->Session->Read('eventPermission') != 'Edit' ) { global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -173,7 +179,9 @@ class EventsController extends AppController {
*/ */
public function edit($id = null) { public function edit($id = null) {
if ( $this->Session->Read('eventPermission') != 'Edit' ) { global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -204,7 +212,9 @@ class EventsController extends AppController {
* @return void * @return void
*/ */
public function delete($id = null) { public function delete($id = null) {
if ( $this->Session->Read('eventPermission') != 'Edit' ) { global $user;
$canEdit = (!$user) || ($user['Events'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -259,7 +269,7 @@ class EventsController extends AppController {
$moreconditions = $moreconditions . ' AND '.$name.$param; $moreconditions = $moreconditions . ' AND '.$name.$param;
} }
$query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;"); $query = $this->Event->query("SELECT MonitorId, COUNT(*) AS Count FROM Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
foreach ($query as $result) { foreach ($query as $result) {
$results[$result['Events']['MonitorId']] = $result[0]['Count']; $results[$result['Events']['MonitorId']] = $result[0]['Count'];
@ -336,7 +346,7 @@ class EventsController extends AppController {
$thumbData['Width'] = (int)$thumbWidth; $thumbData['Width'] = (int)$thumbWidth;
$thumbData['Height'] = (int)$thumbHeight; $thumbData['Height'] = (int)$thumbHeight;
return( $thumbData ); return $thumbData;
} }
public function archive($id = null) { public function archive($id = null) {

View File

@ -30,94 +30,8 @@ class HostController extends AppController {
)); ));
} }
function login() { function login() {
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value'];
if ( $zmOptAuth == '1' ) {
require_once "../../../includes/auth.php";
global $user;
$user = $this->Session->read('user');
$mUser = $this->request->data('user');
$mPassword = $this->request->data('pass');
$mAuth = $this->request->data('auth');
if ( $mUser and $mPassword) {
$user = userLogin($mUser, $mPassword);
if ( !$user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
}
elseif ( $mAuth ) {
$user = getAuthUser($mAuth);
if ( ! $user ) {
throw new UnauthorizedException(__('User not found or incorrect password'));
return;
}
}
else {
throw new UnauthorizedException(__('missing credentials'));
}
if ( 0 and $user ) {
# We have to redo the session variables because cakephp's Session code will overwrite the normal php session
# Actually I'm not sure that is true. Getting indeterminate behaviour
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user', $user) )
$this->log("Error writing session var user");
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user.Username', $user['Username']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('password', $user['Password']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('user.Enabled', $user['Enabled']) )
$this->log("Error writing session var user.Enabled");
if ( ! $this->Session->Write('remoteAddr', $_SERVER['REMOTE_ADDR']) )
$this->log("Error writing session var remoteAddr");
}
// I don't think this is really needed - the Username part
// Enabled check is ok
if ( !$user['Username'] ) {
throw new UnauthorizedException(__('Not Authenticated'));
return;
} else if ( !$user['Enabled'] ) {
throw new UnauthorizedException(__('User is not enabled'));
return;
}
$this->Session->Write('allowedMonitors',$user['MonitorIds']);
$this->Session->Write('streamPermission',$user['Stream']);
$this->Session->Write('eventPermission',$user['Events']);
$this->Session->Write('controlPermission',$user['Control']);
$this->Session->Write('systemPermission',$user['System']);
$this->Session->Write('monitorPermission',$user['Monitors']);
} else {
// if auth is not on, you can do everything
//$userMonitors = $this->User->find('first', $options);
$this->Session->Write('allowedMonitors','');
$this->Session->Write('streamPermission','View');
$this->Session->Write('eventPermission','Edit');
$this->Session->Write('controlPermission','Edit');
$this->Session->Write('systemPermission','Edit');
$this->Session->Write('monitorPermission','Edit');
}
$cred = $this->_getCredentials(); $cred = $this->_getCredentials();
$ver = $this->_getVersion(); $ver = $this->_getVersion();
$this->set(array( $this->set(array(
@ -130,8 +44,7 @@ class HostController extends AppController {
'version', 'version',
'apiversion' 'apiversion'
))); )));
} // end function login()
}
// clears out session // clears out session
function logout() { function logout() {
@ -143,7 +56,7 @@ class HostController extends AppController {
'_serialize' => array('result') '_serialize' => array('result')
)); ));
} } // end function logout()
private function _getCredentials() { private function _getCredentials() {
$credentials = ''; $credentials = '';
@ -167,8 +80,7 @@ class HostController extends AppController {
} }
} }
return array($credentials, $appendPassword); return array($credentials, $appendPassword);
} // end function _getCredentials
}
function getCredentials() { function getCredentials() {
// ignore debug warnings from other functions // ignore debug warnings from other functions
@ -181,8 +93,6 @@ class HostController extends AppController {
) ); ) );
} }
// If $mid is set, only return disk usage for that monitor // If $mid is set, only return disk usage for that monitor
// Else, return an array of total disk usage, and per-monitor // Else, return an array of total disk usage, and per-monitor
// usage. // usage.

View File

@ -21,8 +21,10 @@ class MonitorsController extends AppController {
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission'); global $user;
if ($canView == 'None') { # We already tested for auth in appController, so we just need to test for specific permission
$canView = (!$user) || ($user['Monitors'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
@ -44,8 +46,9 @@ class MonitorsController extends AppController {
$conditions = array(); $conditions = array();
} }
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); global $user;
if (!empty($allowedMonitors)) { $allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$conditions['Monitor.Id' ] = $allowedMonitors; $conditions['Monitor.Id' ] = $allowedMonitors;
} }
$find_array = array('conditions'=>$conditions,'contain'=>array('Group')); $find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
@ -88,8 +91,9 @@ class MonitorsController extends AppController {
if ( !$this->Monitor->exists($id) ) { if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor')); throw new NotFoundException(__('Invalid monitor'));
} }
$allowedMonitors=preg_split('@,@', $this->Session->Read('allowedMonitors'), NULL, PREG_SPLIT_NO_EMPTY); global $user;
if ( !empty($allowedMonitors) ) { $allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors); $restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors);
} else { } else {
$restricted = ''; $restricted = '';
@ -115,7 +119,9 @@ class MonitorsController extends AppController {
public function add() { public function add() {
if ( $this->request->is('post') ) { if ( $this->request->is('post') ) {
if ( $this->Session->Read('systemPermission') != 'Edit' ) { global $user;
$canAdd = (!$user) || ($user['System'] == 'Edit' );
if ( !$canAdd ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -148,7 +154,9 @@ class MonitorsController extends AppController {
if ( !$this->Monitor->exists($id) ) { if ( !$this->Monitor->exists($id) ) {
throw new NotFoundException(__('Invalid monitor')); throw new NotFoundException(__('Invalid monitor'));
} }
if ( $this->Session->Read('monitorPermission') != 'Edit' ) { global $user;
$canEdit = (!$user) || ($user['Monitors'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -215,7 +223,7 @@ class MonitorsController extends AppController {
} }
public function sourceTypes() { public function sourceTypes() {
$sourceTypes = $this->Monitor->query("describe Monitors Type;"); $sourceTypes = $this->Monitor->query('describe Monitors Type;');
preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches); preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches);
foreach( explode(',', $matches[1]) as $value ) { foreach( explode(',', $matches[1]) as $value ) {
@ -264,7 +272,6 @@ class MonitorsController extends AppController {
$config = $this->Config->find('first', $options); $config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value']; $zmOptAuth = $config['Config']['Value'];
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')); $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$config = $this->Config->find('first', $options); $config = $this->Config->find('first', $options);
$zmAuthRelay = $config['Config']['Value']; $zmAuthRelay = $config['Config']['Value'];
@ -372,5 +379,4 @@ class MonitorsController extends AppController {
$status = exec( $shellcmd ); $status = exec( $shellcmd );
} }
} }
} // end class MonitorsController } // end class MonitorsController

View File

@ -8,7 +8,6 @@ App::uses('AppController', 'Controller');
*/ */
class ServersController extends AppController { class ServersController extends AppController {
/** /**
* Components * Components
* *
@ -16,18 +15,16 @@ class ServersController extends AppController {
*/ */
public $components = array('Paginator', 'RequestHandler'); public $components = array('Paginator', 'RequestHandler');
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('streamPermission'); global $user;
if ($canView =='None') { $canView = (!$user) || ($user['System'] != 'None');
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
} }
/** /**
* index method * index method
* *
@ -78,8 +75,9 @@ public function beforeFilter() {
public function add() { public function add() {
if ( $this->request->is('post') ) { if ( $this->request->is('post') ) {
if ($this->Session->Read('systemPermission') != 'Edit') global $user;
{ $canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -103,14 +101,16 @@ public function beforeFilter() {
public function edit($id = null) { public function edit($id = null) {
$this->Server->id = $id; $this->Server->id = $id;
if (!$this->Server->exists($id)) { global $user;
throw new NotFoundException(__('Invalid server')); $canEdit = (!$user) || ($user['System'] == 'Edit');
} if ( !$canEdit ) {
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
if ( !$this->Server->exists($id) ) {
throw new NotFoundException(__('Invalid server'));
}
if ( $this->Server->save($this->request->data) ) { if ( $this->Server->save($this->request->data) ) {
$message = 'Saved'; $message = 'Saved';
} else { } else {
@ -133,15 +133,17 @@ public function beforeFilter() {
* @return void * @return void
*/ */
public function delete($id = null) { public function delete($id = null) {
global $user;
$canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->Server->id = $id; $this->Server->id = $id;
if ( !$this->Server->exists() ) { if ( !$this->Server->exists() ) {
throw new NotFoundException(__('Invalid server')); throw new NotFoundException(__('Invalid server'));
} }
if ($this->Session->Read('systemPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;
}
$this->request->allowMethod('post', 'delete'); $this->request->allowMethod('post', 'delete');
#$this->daemonControl($this->Server->id, 'stop'); #$this->daemonControl($this->Server->id, 'stop');

View File

@ -13,16 +13,14 @@ public $components = array('RequestHandler');
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('systemPermission'); global $user;
if ($canView =='None') $canView = (!$user) || ($user['System'] != 'None');
{ if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
} }
/** /**
* index method * index method
* *
@ -86,8 +84,9 @@ public function beforeFilter() {
throw new NotFoundException(__('Invalid state')); throw new NotFoundException(__('Invalid state'));
} }
if ($this->Session->Read('systemPermission') != 'Edit') global $user;
{ $canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -111,8 +110,9 @@ public function beforeFilter() {
*/ */
public function delete($id = null) { public function delete($id = null) {
$this->State->id = $id; $this->State->id = $id;
if ($this->Session->Read('systemPermission') != 'Edit') global $user;
{ $canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }
@ -129,8 +129,9 @@ public function beforeFilter() {
} }
public function change() { public function change() {
if ($this->Session->Read('systemPermission') != 'Edit') global $user;
{ $canEdit = (!$user) || ($user['System'] == 'Edit');
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient privileges')); throw new UnauthorizedException(__('Insufficient privileges'));
return; return;
} }

View File

@ -16,8 +16,10 @@ class ZonesController extends AppController {
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission');
if ( $canView =='None' ) { global $user;
$canView = (!$user) || $user['Monitors'] != 'None';
if ( !$canView ) {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
@ -38,12 +40,12 @@ class ZonesController extends AppController {
'_serialize' => array('zones') '_serialize' => array('zones')
)); ));
} }
public function index() { public function index() {
$this->Zone->recursive = -1; $this->Zone->recursive = -1;
$allowedMonitors = preg_split('@,@', $this->Session->Read('allowedMonitors'), NULL, PREG_SPLIT_NO_EMPTY); global $user;
if ( !empty($allowedMonitors) ) { $allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'],NULL, PREG_SPLIT_NO_EMPTY) : null;
if ( $allowedMonitors ) {
$mon_options = array('Zones.MonitorId' => $allowedMonitors); $mon_options = array('Zones.MonitorId' => $allowedMonitors);
} else { } else {
$mon_options = ''; $mon_options = '';
@ -62,6 +64,14 @@ class ZonesController extends AppController {
*/ */
public function add() { public function add() {
if ( $this->request->is('post') ) { if ( $this->request->is('post') ) {
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
$this->Zone->create(); $this->Zone->create();
if ( $this->Zone->save($this->request->data) ) { if ( $this->Zone->save($this->request->data) ) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index')); return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
@ -85,6 +95,12 @@ class ZonesController extends AppController {
throw new NotFoundException(__('Invalid zone')); throw new NotFoundException(__('Invalid zone'));
} }
if ( $this->request->is(array('post', 'put')) ) { if ( $this->request->is(array('post', 'put')) ) {
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->Zone->save($this->request->data) ) { if ( $this->Zone->save($this->request->data) ) {
return $this->flash(__('The zone has been saved.'), array('action' => 'index')); return $this->flash(__('The zone has been saved.'), array('action' => 'index'));
} }
@ -109,6 +125,12 @@ class ZonesController extends AppController {
throw new NotFoundException(__('Invalid zone')); throw new NotFoundException(__('Invalid zone'));
} }
$this->request->allowMethod('post', 'delete'); $this->request->allowMethod('post', 'delete');
global $user;
$canEdit = (!$user) || $user['Monitors'] == 'Edit';
if ( !$canEdit ) {
throw new UnauthorizedException(__('Insufficient Privileges'));
return;
}
if ( $this->Zone->delete() ) { if ( $this->Zone->delete() ) {
return $this->flash(__('The zone has been deleted.'), array('action' => 'index')); return $this->flash(__('The zone has been deleted.'), array('action' => 'index'));
} else { } else {
@ -144,4 +166,4 @@ class ZonesController extends AppController {
'_serialize' => array('status') '_serialize' => array('status')
)); ));
} }
} } // end class

View File

@ -110,7 +110,7 @@ class Monitor extends AppModel {
); );
public $actsAs = array( public $actsAs = array(
'CakePHP-Enum-Behavior.Enum' => 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'), 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'),
'Orientation' => array('0','90','180','270','hori','vert'), 'Orientation' => array('0','90','180','270','hori','vert'),
'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'),

@ -1 +1 @@
Subproject commit ea90c0cd7f6e24333a90885e563b5d30b793db29 Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237

@ -1 +1 @@
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef Subproject commit c3976f1478c681b0bbc132ec3a3e82c3984eeed5

View File

@ -12,6 +12,9 @@ private $defaults = array(
'CanMoveRel' => 0, 'CanMoveRel' => 0,
'CanMoveCon' => 0, 'CanMoveCon' => 0,
'CanPan' => 0, 'CanPan' => 0,
'CanReset' => 0,
'CanSleep' => 0,
'CanWake' => 0,
'MinPanRange' => NULL, 'MinPanRange' => NULL,
'MaxPanRange' => NULL, 'MaxPanRange' => NULL,
'MinPanStep' => NULL, 'MinPanStep' => NULL,
@ -103,7 +106,7 @@ private $defaults = array(
if ( $IdOrRow ) { if ( $IdOrRow ) {
$row = NULL; $row = NULL;
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT * FROM Control WHERE Id=?', NULL, array( $IdOrRow ) ); $row = dbFetchOne( 'SELECT * FROM Controls WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) { if ( ! $row ) {
Error("Unable to load Control record for Id=" . $IdOrRow ); Error("Unable to load Control record for Id=" . $IdOrRow );
} }
@ -163,8 +166,7 @@ private $defaults = array(
} }
} }
} }
public static function find_all( $parameters = null, $options = null ) { public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Controls '; $sql = 'SELECT * FROM Controls ';
$values = array(); $values = array();
@ -186,15 +188,37 @@ private $defaults = array(
} }
$sql .= implode(' AND ', $fields ); $sql .= implode(' AND ', $fields );
} }
if ( $options and isset($options['order']) ) { if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $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); $result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control'); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control');
foreach ( $results as $row => $obj ) { 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 ) { public function save( $new_values = null ) {

View File

@ -84,7 +84,12 @@ class Event {
} }
public function Monitor() { 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){ public function __call( $fn, array $args){
@ -95,9 +100,13 @@ class Event {
return $this->{$fn}; return $this->{$fn};
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
$file = $backTrace[0]['file'];
$line = $backTrace[0]['line'];
Warning("Unknown function call Event->$fn from $file:$line");
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Warning("Unknown function call Event->$fn from $file:$line"); Warning("Unknown function call Event->$fn from $file:$line");
Warning(print_r( $this, true ));
} }
} }
@ -486,7 +495,7 @@ class Event {
isset($event_cache[$parameters['Id']]) ) { isset($event_cache[$parameters['Id']]) ) {
return $event_cache[$parameters['Id']]; return $event_cache[$parameters['Id']];
} }
$results = Event::find_all( $parameters, $options ); $results = Event::find( $parameters, $options );
if ( count($results) > 1 ) { if ( count($results) > 1 ) {
Error("Event Returned more than 1"); Error("Event Returned more than 1");
return $results[0]; return $results[0];
@ -497,8 +506,7 @@ class Event {
} }
} }
public static function find_all( $parameters = null, $options = null ) { public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Events '; $sql = 'SELECT * FROM Events ';
$values = array(); $values = array();
@ -520,13 +528,27 @@ class Event {
} }
$sql .= implode(' AND ', $fields ); $sql .= implode(' AND ', $fields );
} }
if ( $options and isset($options['order']) ) { if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $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); $result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Event'); $results = $result->fetchALL();
foreach ( $results as $row => $obj ) { foreach ( $results as $row ) {
$filters[] = $obj; $filters[] = new Event($row);
} }
return $filters; return $filters;
} }

View File

@ -11,6 +11,7 @@ public $defaults = array(
'AutoDelete' => 0, 'AutoDelete' => 0,
'AutoArchive' => 0, 'AutoArchive' => 0,
'AutoVideo' => 0, 'AutoVideo' => 0,
'AutoUpload' => 0,
'AutoMessage' => 0, 'AutoMessage' => 0,
'AutoMove' => 0, 'AutoMove' => 0,
'AutoMoveTo' => 0, 'AutoMoveTo' => 0,
@ -111,15 +112,59 @@ public $defaults = array(
return $this->defaults{'limit'}; return $this->defaults{'limit'};
} }
public static function find_all() { public static function find( $parameters = null, $options = null ) {
$filters = array(); $filters = array();
$result = dbQuery( 'SELECT * FROM Filters ORDER BY Name'); $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'); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter');
foreach ( $results as $row => $obj ) { foreach ( $results as $row => $obj ) {
$filters[] = $obj; $filters[] = $obj;
} }
return $filters; 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() { public function delete() {
dbQuery('DELETE FROM Filters WHERE Id = ?', array($this->{'Id'})); dbQuery('DELETE FROM Filters WHERE Id = ?', array($this->{'Id'}));
@ -140,9 +185,7 @@ public $defaults = array(
$this->{$k} = $v; $this->{$k} = $v;
} }
} }
} } # end function set
} # end class Filter
} # end class
?> ?>

View File

@ -58,27 +58,7 @@ class Group {
} }
} }
public static function find_one($parameters = null, $options = null) { public static function find( $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();
$sql = 'SELECT * FROM Groups '; $sql = 'SELECT * FROM Groups ';
$values = array(); $values = array();
@ -98,14 +78,49 @@ class Group {
} }
} }
$sql .= implode(' AND ', $fields); $sql .= implode(' AND ', $fields);
} # end if parameters
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
} }
$sql .= ' ORDER BY Name'; 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); $result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Group'); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Group');
foreach ( $results as $row => $obj ) { 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() { public function delete() {
@ -182,7 +197,7 @@ class Group {
public static function get_dropdown_options() { public static function get_dropdown_options() {
$Groups = array(); $Groups = array();
foreach ( Group::find_all( ) as $Group ) { foreach ( Group::find( ) as $Group ) {
$Groups[$Group->Id()] = $Group; $Groups[$Group->Id()] = $Group;
} }

View File

@ -2,6 +2,8 @@
require_once('database.php'); require_once('database.php');
require_once('Server.php'); require_once('Server.php');
$monitor_cache = array();
class Monitor { class Monitor {
private $defaults = array( private $defaults = array(
@ -9,17 +11,19 @@ private $defaults = array(
'Name' => '', 'Name' => '',
'StorageId' => 0, 'StorageId' => 0,
'ServerId' => 0, 'ServerId' => 0,
'Type' => 'Ffmpeg',
'Function' => 'None', 'Function' => 'None',
'Enabled' => 1, 'Enabled' => 1,
'LinkedMonitors' => null,
'Width' => null, 'Width' => null,
'Height' => null, 'Height' => null,
'Orientation' => null, 'Orientation' => null,
'AnalysisFPSLimit' => null, 'AnalysisFPSLimit' => null,
'ZoneCount' => 0, 'ZoneCount' => 0,
'Triggers' => null, 'Triggers' => null,
'Type' => 'Ffmpeg',
'MaxFPS' => null, 'MaxFPS' => null,
'AlarmMaxFPS' => null, 'AlarmMaxFPS' => null,
'Refresh' => null,
); );
private $status_fields = array( private $status_fields = array(
'AnalysisFPS' => null, 'AnalysisFPS' => null,
@ -163,6 +167,8 @@ private $control_fields = array(
} }
} }
} }
global $monitor_cache;
$monitor_cache[$row['Id']] = $this;
} else { } else {
Error('No row for Monitor ' . $IdOrRow); Error('No row for Monitor ' . $IdOrRow);
@ -276,8 +282,7 @@ private $control_fields = array(
} # end if method_exists } # end if method_exists
} # end foreach $data as $k=>$v } # end foreach $data as $k=>$v
} }
public static function find_all( $parameters = null, $options = null ) { public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Monitors '; $sql = 'SELECT * FROM Monitors ';
$values = array(); $values = array();
@ -299,16 +304,45 @@ private $control_fields = array(
} }
$sql .= implode(' AND ', $fields); $sql .= implode(' AND ', $fields);
} }
if ( $options and isset($options['order']) ) { if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $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); $result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Monitor'); $results = $result->fetchALL();
foreach ( $results as $row => $obj ) { foreach ( $results as $row ) {
$filters[] = $obj; $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) { public function save($new_values = null) {
@ -487,8 +521,12 @@ private $control_fields = array(
$source = preg_replace( '/^.*\//', '', $this->{'Path'} ); $source = preg_replace( '/^.*\//', '', $this->{'Path'} );
} elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { } elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) {
$url_parts = parse_url( $this->{'Path'} ); $url_parts = parse_url( $this->{'Path'} );
if ( ZM_WEB_FILTER_SOURCE == "Hostname" ) { # Filter out everything but the hostname if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { # Filter out everything but the hostname
if ( isset($url_parts['host']) ) {
$source = $url_parts['host']; $source = $url_parts['host'];
} else {
$source = $this->{'Path'};
}
} elseif ( ZM_WEB_FILTER_SOURCE == "NoCredentials" ) { # Filter out sensitive and common items } elseif ( ZM_WEB_FILTER_SOURCE == "NoCredentials" ) { # Filter out sensitive and common items
unset($url_parts['user']); unset($url_parts['user']);
unset($url_parts['pass']); unset($url_parts['pass']);
@ -507,5 +545,10 @@ private $control_fields = array(
} }
return $source; return $source;
} // end function Source } // end function Source
public function Url() {
return $this->Server()->Url() .':'. ( ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : $_SERVER['SERVER_PORT'] );
}
} // end class Monitor } // end class Monitor
?> ?>

View File

@ -1,6 +1,8 @@
<?php <?php
require_once('database.php'); require_once('database.php');
$server_cache = array();
class Server { class Server {
private $defaults = array( private $defaults = array(
'Id' => null, 'Id' => null,
@ -10,7 +12,10 @@ class Server {
'zmstats' => 1, 'zmstats' => 1,
'zmtrigger' => 0, 'zmtrigger' => 0,
); );
public function __construct( $IdOrRow = NULL ) { public function __construct( $IdOrRow = NULL ) {
global $server_cache;
$row = NULL; $row = NULL;
if ( $IdOrRow ) { if ( $IdOrRow ) {
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) { if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
@ -26,44 +31,12 @@ class Server {
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
$this->{$k} = $v; $this->{$k} = $v;
} }
$server_cache[$row['Id']] = $this;
} else { } else {
$this->{'Name'} = ''; $this->{'Name'} = '';
$this->{'Hostname'} = ''; $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() {
if ( $this->Id() ) { if ( $this->Id() ) {
@ -96,34 +69,61 @@ class Server {
} }
} }
} }
public static function find( $parameters = null, $options = null ) {
public static function find( $parameters = array(), $limit = NULL ) { $filters = array();
$sql = 'SELECT * FROM Servers '; $sql = 'SELECT * FROM Servers ';
$values = array(); $values = array();
if ( sizeof($parameters) ) {
$sql .= ' WHERE ' . implode( ' AND ', array_map( if ( $parameters ) {
function($v){ return $v.'=?'; }, $fields = array();
array_keys( $parameters ) $sql .= 'WHERE ';
) ); foreach ( $parameters as $field => $value ) {
$values = array_values( $parameters ); 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;
} }
if ( is_integer( $limit ) or ctype_digit( $limit ) ) { }
$sql .= ' LIMIT ' . $limit; $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 { } else {
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Error("Invalid value for limit($limit) passed to Server::find from $file:$line"); Error("Invalid value for limit(".$options['limit'].") passed to Server::find from $file:$line");
return; return array();
}
}
} }
$results = dbFetchAll( $sql, NULL, $values ); $results = dbFetchAll( $sql, NULL, $values );
if ( $results ) { if ( $results ) {
return array_map( function($id){ return new Server($id); }, $results ); return array_map( function($id){ return new Server($id); }, $results );
} }
return array();
} }
public static function find_one( $parameters = array() ) { public static function find_one( $parameters = array() ) {
$results = Server::find( $parameters, 1 ); 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) ) { if ( ! sizeof($results) ) {
return; return;
} }

View File

@ -3,6 +3,18 @@ require_once('database.php');
$storage_cache = array(); $storage_cache = array();
class Storage { 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 ) { public function __construct( $IdOrRow = NULL ) {
global $storage_cache; global $storage_cache;
@ -11,7 +23,7 @@ class Storage {
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) { if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
$row = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($IdOrRow)); $row = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($IdOrRow));
if ( ! $row ) { 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) ) { } else if ( is_array($IdOrRow) ) {
$row = $IdOrRow; $row = $IdOrRow;
@ -40,7 +52,6 @@ class Storage {
$this->{'Path'} = ZM_DIR_EVENTS; $this->{'Path'} = ZM_DIR_EVENTS;
} }
return $this->{'Path'}; return $this->{'Path'};
} }
return $this->{'Name'}; return $this->{'Name'};
} }
@ -57,15 +68,21 @@ class Storage {
if ( count($args) ) { if ( count($args) ) {
$this->{$fn} = $args[0]; $this->{$fn} = $args[0];
} }
if ( array_key_exists( $fn, $this ) ) { if ( array_key_exists($fn, $this) )
return $this->{$fn}; return $this->{$fn};
if ( array_key_exists( $fn, $this->defaults ) )
return $this->defaults{$fn};
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
$file = $backTrace[0]['file'];
$line = $backTrace[0]['line'];
Warning("Unknown function call Storage->$fn from $file:$line");
$file = $backTrace[1]['file']; $file = $backTrace[1]['file'];
$line = $backTrace[1]['line']; $line = $backTrace[1]['line'];
Warning("Unknown function call Storage->$fn from $file:$line"); Warning("Unknown function call Storage->$fn from $file:$line");
} }
}
public static function find_one( $parameters = null, $options = null ) { public static function find_one( $parameters = null, $options = null ) {
global $storage_cache; global $storage_cache;
if ( if (
@ -74,7 +91,8 @@ class Storage {
isset($storage_cache[$parameters['Id']]) ) { isset($storage_cache[$parameters['Id']]) ) {
return $storage_cache[$parameters['Id']]; return $storage_cache[$parameters['Id']];
} }
$results = Storage::find_all( $parameters, $options );
$results = Storage::find($parameters, $options);
if ( count($results) > 1 ) { if ( count($results) > 1 ) {
Error("Storage Returned more than 1"); Error("Storage Returned more than 1");
return $results[0]; return $results[0];
@ -84,8 +102,7 @@ class Storage {
return null; return null;
} }
} }
public static function find_all( $parameters = null, $options = null ) { public static function find( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Storage '; $sql = 'SELECT * FROM Storage ';
$values = array(); $values = array();
@ -106,23 +123,38 @@ class Storage {
} }
} }
$sql .= implode(' AND ', $fields); $sql .= implode(' AND ', $fields);
} } # end if parameters
if ( $options and isset($options['order']) ) { if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $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); $result = dbQuery($sql, $values);
if ( $result ) { if ( $result ) {
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage'); $results = $result->fetchALL();
foreach ( $results as $row => $obj ) { foreach ( $results as $row ) {
$filters[] = $obj; $storage_areas[] = new Storage($row);
} }
} }
return $filters; return $storage_areas;
} } # end find()
public function disk_usage_percent() { public function disk_usage_percent() {
$path = $this->Path(); $path = $this->Path();
if ( ! $path ) { if ( ! $path ) {
Warning("Storage::disk_usage_percent: path is empty"); Warning('Storage::disk_usage_percent: path is empty');
return 0; return 0;
} else if ( ! file_exists($path) ) { } else if ( ! file_exists($path) ) {
Warning("Storage::disk_usage_percent: path $path does not exist"); Warning("Storage::disk_usage_percent: path $path does not exist");
@ -131,7 +163,7 @@ class Storage {
$total = $this->disk_total_space(); $total = $this->disk_total_space();
if ( ! $total ) { if ( ! $total ) {
Error("disk_total_space returned false for " . $path ); Error('disk_total_space returned false for ' . $path );
return 0; return 0;
} }
$used = $this->disk_used_space(); $used = $this->disk_used_space();
@ -139,6 +171,7 @@ class Storage {
//Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )"); //Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )");
return $usage; return $usage;
} }
public function disk_total_space() { public function disk_total_space() {
if ( !array_key_exists('disk_total_space', $this) ) { if ( !array_key_exists('disk_total_space', $this) ) {
$path = $this->Path(); $path = $this->Path();
@ -175,7 +208,7 @@ class Storage {
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) { 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()) ); $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 ) { foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) {
$Event->Storage($this); // Prevent further db hit $Event->Storage($this); // Prevent further db hit
$used += $Event->DiskSpace(); $used += $Event->DiskSpace();
} }

View File

@ -210,11 +210,13 @@ if ( canView('Events') ) {
dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid'])); dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
} else if ( $action == 'eventdetail' ) { } else if ( $action == 'eventdetail' ) {
if ( !empty($_REQUEST['eid']) ) { 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 { } else {
$dbConn->beginTransaction(); $dbConn->beginTransaction();
foreach( getAffectedIds('markEid') as $markEid ) { 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(); $dbConn->commit();
} }
@ -265,7 +267,9 @@ if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
$zmuOutput = exec($zmuCommand); $zmuOutput = exec($zmuCommand);
list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput); list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput);
dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid)); dbQuery(
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
array($brightness, $contrast, $hue, $colour, $mid));
} }
} }
@ -282,8 +286,8 @@ if ( canEdit('Control') ) {
} elseif ( $action == 'delete' ) { } elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markCids']) ) { if ( isset($_REQUEST['markCids']) ) {
foreach( $_REQUEST['markCids'] as $markCid ) { foreach( $_REQUEST['markCids'] as $markCid ) {
dbQuery( 'delete from Controls where Id = ?', array($markCid) ); dbQuery('DELETE FROM Controls WHERE Id = ?', array($markCid));
dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) ); dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($markCid));
$refreshParent = true; $refreshParent = true;
} }
} }
@ -310,7 +314,6 @@ if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
$Monitor->zmaControl('start'); $Monitor->zmaControl('start');
} }
} }
} // end foreach mid } // end foreach mid
$refreshParent = true; $refreshParent = true;
} // end if action == save } // end if action == save
@ -328,11 +331,12 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
$oldFunction = $monitor['Function']; $oldFunction = $monitor['Function'];
$oldEnabled = $monitor['Enabled']; $oldEnabled = $monitor['Enabled'];
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { 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['Function'] = $newFunction;
$monitor['Enabled'] = $newEnabled; $monitor['Enabled'] = $newEnabled;
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
$restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
zmaControl($monitor, 'stop'); zmaControl($monitor, 'stop');
zmcControl($monitor, $restart?'restart':''); zmcControl($monitor, $restart?'restart':'');
@ -373,16 +377,16 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} else { } 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' ) { if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
zmaControl($monitor, 'stop'); zmaControl($monitor, 'stop');
zmcControl($monitor, 'restart'); zmcControl($monitor, 'restart');
zmaControl($monitor, 'start'); zmaControl($monitor, 'start');
} else { } else {
zmaControl( $mid, 'restart' ); zmaControl($monitor, 'restart');
} }
} }
if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) { if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor['Controllable'] ) {
require_once('control_functions.php'); require_once('control_functions.php');
sendControlCommand($mid, 'quit'); sendControlCommand($mid, 'quit');
} }
@ -401,19 +405,19 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
} }
} }
if ( $changes > 0 ) { if ( $changes > 0 ) {
if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) {
zmaControl($mid, 'restart'); zmaControl($mid, 'restart');
} }
$refreshParent = true; $refreshParent = true;
} }
$view = 'none'; $view = 'none';
} elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) { } elseif ( ($action == 'sequence') && isset($_REQUEST['smid']) ) {
$smid = validInt($_REQUEST['smid']); $smid = validInt($_REQUEST['smid']);
$monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) ); $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid));
$smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) ); $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($smonitor['Sequence'], $monitor['Id']));
dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) ); dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($monitor['Sequence'], $smonitor['Id']));
$refreshParent = true; $refreshParent = true;
fixSequences(); fixSequences();
@ -421,8 +425,8 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
if ( isset($_REQUEST['markZids']) ) { if ( isset($_REQUEST['markZids']) ) {
$deletedZid = 0; $deletedZid = 0;
foreach ( $_REQUEST['markZids'] as $markZid ) { foreach ( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) ); $zone = dbFetchOne('SELECT * FROM Zones WHERE Id=?', NULL, array($markZid));
dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) ); dbQuery('DELETE FROM Zones WHERE MonitorId=? AND Id=?', array($mid, $markZid));
$deletedZid = 1; $deletedZid = 1;
} }
if ( $deletedZid ) { if ( $deletedZid ) {
@ -476,11 +480,12 @@ if ( canEdit( 'Monitors' ) ) {
); );
if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { 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'); $_REQUEST['newMonitor']['ServerId'] = dbFetchOne(
Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); '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') ) { if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
$_REQUEST['newMonitor']['ServerId'] = 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);
} }
} }
@ -537,12 +542,14 @@ if ( canEdit( 'Monitors' ) ) {
$changes = getFormChanges($zone, $newZone, $types); $changes = getFormChanges($zone, $newZone, $types);
if ( count($changes) ) { if ( count($changes) ) {
dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) ); dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
} array($mid, $zone['Id']));
}
} }
} // end foreach zone
} // end if width and height
$restart = true; $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. # FIXME This is actually a race condition. Should lock the table.
$maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence'); $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
$changes[] = 'Sequence = '.($maxSeq+1); $changes[] = 'Sequence = '.($maxSeq+1);
@ -550,7 +557,7 @@ if ( canEdit( 'Monitors' ) ) {
if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) { if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) {
$mid = dbInsertId(); $mid = dbInsertId();
$zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; $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'; //$view = 'none';
$Storage = new Storage($_REQUEST['newMonitor']['StorageId']); $Storage = new Storage($_REQUEST['newMonitor']['StorageId']);
mkdir($Storage->Path().'/'.$mid, 0755); mkdir($Storage->Path().'/'.$mid, 0755);
@ -558,17 +565,17 @@ if ( canEdit( 'Monitors' ) ) {
symlink($mid, $Storage->Path().'/'.$saferName); symlink($mid, $Storage->Path().'/'.$saferName);
} else { } else {
Error("Error saving new Monitor."); Error('Error saving new Monitor.');
return; return;
} }
} else { } else {
Error("Users with Monitors restrictions cannot create new monitors."); Error('Users with Monitors restrictions cannot create new monitors.');
return; return;
} }
$restart = true; $restart = true;
} else { } 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) } # end if count(changes)
if ( if (
@ -593,17 +600,17 @@ if ( canEdit( 'Monitors' ) ) {
if ( count($x10Changes) ) { if ( count($x10Changes) ) {
if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) { 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'] ) { } elseif ( !$user['MonitorIds'] ) {
if ( !$x10Monitor ) { if ( !$x10Monitor ) {
dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) ); dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid));
} else { } else {
dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) ); dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid));
} }
} }
$restart = true; $restart = true;
} } # end if has x10Changes
} } # end if ZM_OPT_X10
if ( $restart ) { if ( $restart ) {
@ -647,9 +654,11 @@ if ( canEdit( 'Devices' ) ) {
setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']); setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']);
} else if ( isset($_REQUEST['newDevice']) ) { } else if ( isset($_REQUEST['newDevice']) ) {
if ( isset($_REQUEST['did']) ) { 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 { } 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; $refreshParent = true;
$view = 'none'; $view = 'none';
@ -657,7 +666,7 @@ if ( canEdit( 'Devices' ) ) {
} elseif ( $action == 'delete' ) { } elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['markDids']) ) { if ( isset($_REQUEST['markDids']) ) {
foreach( $_REQUEST['markDids'] as $markDid ) { foreach( $_REQUEST['markDids'] as $markDid ) {
dbQuery( 'delete from Devices where Id=?', array($markDid) ); dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid));
$refreshParent = true; $refreshParent = true;
} }
} }
@ -665,7 +674,7 @@ if ( canEdit( 'Devices' ) ) {
} // end if canedit devices } // end if canedit devices
// Group view actions // Group view actions
if ( canView( 'Groups' ) && $action == 'setgroup' ) { if ( canView('Groups') && ($action == 'setgroup') ) {
if ( !empty($_REQUEST['gid']) ) { 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 { } else {
@ -675,19 +684,31 @@ if ( canView( 'Groups' ) && $action == 'setgroup' ) {
} }
// Group edit actions // 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 # 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 ( canEdit('Groups') ) {
if ( $action == 'group' ) { if ( $action == 'group' ) {
$monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
$group_id = null; $group_id = null;
if ( !empty($_POST['gid']) ) { if ( !empty($_POST['gid']) ) {
$group_id = $_POST['gid']; $group_id = $_POST['gid'];
dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', dbQuery(
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) ); '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('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id));
} else { } else {
dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', dbQuery(
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) ); 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
array(
$_POST['newGroup']['Name'],
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
)
);
$group_id = dbInsertId(); $group_id = dbInsertId();
} }
if ( $group_id ) { if ( $group_id ) {
@ -738,19 +759,24 @@ if ( canEdit( 'System' ) ) {
} else if ( $_REQUEST['object'] == 'server' ) { } else if ( $_REQUEST['object'] == 'server' ) {
if ( $action == 'Save' ) { if ( $action == 'Save' ) {
if ( !empty($_REQUEST['id']) ) if ( !empty($_REQUEST['id']) ) {
$dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) ); $dbServer = dbFetchOne(
else 'SELECT * FROM Servers WHERE Id=?',
NULL,
array($_REQUEST['id']) );
} else {
$dbServer = array(); $dbServer = array();
}
$types = 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']) ) { 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 { } else {
dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) ); dbQuery('INSERT INTO Servers SET '.implode(', ', $changes));
} }
$refreshParent = true; $refreshParent = true;
} }
@ -804,7 +830,7 @@ if ( canEdit( 'System' ) ) {
} }
case 'ignore' : 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; break;
} }
case 'hour' : case 'hour' :
@ -819,12 +845,12 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'week' ) { } elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60; $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; break;
} }
case 'never' : 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; break;
} }
} }
@ -852,17 +878,38 @@ if ( canEdit( 'System' ) ) {
} elseif ( $option == 'month' ) { } elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60; $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; break;
} }
case 'never' : case 'never' :
case 'already' : 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; break;
} }
} // end switch option } // 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']) ) { if ( $action == 'options' && isset($_REQUEST['tab']) ) {
$configCat = $configCats[$_REQUEST['tab']]; $configCat = $configCats[$_REQUEST['tab']];
$changed = false; $changed = false;
@ -902,9 +949,10 @@ if ( canEdit( 'System' ) ) {
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab']; $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab'];
} }
loadConfig(false); loadConfig(false);
return;
} elseif ( $action == 'user' ) { } elseif ( $action == 'user' ) {
if ( !empty($_REQUEST['uid']) ) 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 else
$dbUser = array(); $dbUser = array();
@ -918,12 +966,12 @@ if ( canEdit( 'System' ) ) {
if ( count($changes) ) { if ( count($changes) ) {
if ( !empty($_REQUEST['uid']) ) { 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 we are updating the logged in user, then update our session user data.
if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) if ( $user and ( $dbUser['Username'] == $user['Username'] ) )
userLogin($dbUser['Username'], $dbUser['Password']); userLogin($dbUser['Username'], $dbUser['Password']);
} else { } else {
dbQuery( 'insert into Users set '.implode( ', ', $changes ) ); dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
} }
$refreshParent = true; $refreshParent = true;
} }
@ -938,22 +986,21 @@ if ( canEdit( 'System' ) ) {
if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) { if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) {
$sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id'; $sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id';
$definitions = array(); $definitions = array();
foreach( dbFetchAll( $sql ) as $monitor ) foreach( dbFetchAll($sql) as $monitor ) {
{
$definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled']; $definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled'];
} }
$definition = join(',', $definitions); $definition = join(',', $definitions);
if ( $_REQUEST['newState'] ) if ( $_REQUEST['newState'] )
$_REQUEST['runState'] = $_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' ) { } elseif ( $action == 'delete' ) {
if ( isset($_REQUEST['runState']) ) 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']) ) { if ( isset($_REQUEST['markUids']) ) {
foreach( $_REQUEST['markUids'] as $markUid ) 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'] ) if ( $markUid == $user['Id'] )
userLogout(); userLogout();
} }
@ -972,7 +1019,7 @@ if ( canEdit( 'System' ) ) {
else else
unset($changes['Password']); unset($changes['Password']);
if ( count($changes) ) { if ( count($changes) ) {
dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) ); dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
$refreshParent = true; $refreshParent = true;
} }
$view = 'none'; $view = 'none';

View File

@ -212,7 +212,7 @@ function process_configfile($configFile) {
continue; continue;
elseif ( preg_match( '/^\s*#/', $str )) elseif ( preg_match( '/^\s*#/', $str ))
continue; continue;
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches ))
$configvals[$matches[1]] = $matches[2]; $configvals[$matches[1]] = $matches[2];
} }
fclose( $cfg ); fclose( $cfg );

View File

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

View File

@ -47,10 +47,11 @@ function CORSHeaders() {
} }
foreach( $servers as $row ) { foreach( $servers as $row ) {
$Server = new Server($row); $Server = new Server($row);
if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { if ( preg_match('/^'.preg_quote($Server->Url(),'/').'/', $_SERVER['HTTP_ORIGIN']) ) {
$valid = true; $valid = true;
header('Access-Control-Allow-Origin: ' . $Server->Url() ); header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Headers: x-requested-with,x-request'); header('Access-Control-Allow-Headers: x-requested-with,x-request');
break;
} }
} }
if ( !$valid ) { if ( !$valid ) {
@ -436,16 +437,34 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
} }
function htmlOptions($contents, $values) { function htmlOptions($contents, $values) {
$html = ''; $options_html = '';
foreach ( $contents as $value=>$text ) {
if ( is_array( $text ) ) foreach ( $contents as $value=>$option ) {
$text = $text['Name']; $disabled = 0;
else if ( is_object( $text ) ) $text = '';
$text = $text->Name(); if ( is_array($option) ) {
$selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values);
$html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>"; if ( isset($option['Name']) )
$text = $option['Name'];
else if ( isset($option['text']) )
$text = $option['text'];
if ( isset($option['disabled']) ) {
$disabled = $option['disabled'];
Error("Setting to disabled");
} }
return $html; } else if ( is_object($option) ) {
$text = $option->Name();
} else {
$text = $option;
}
$selected = is_array($values) ? in_array($value, $values) : !strcmp($value, $values);
$options_html .= "<option value=\"$value\"".
($selected?' selected="selected"':'').
($disabled?' disabled="disabled"':'').
">$text</option>";
}
return $options_html;
} }
function truncText( $text, $length, $deslash=1 ) { function truncText( $text, $length, $deslash=1 ) {
@ -892,7 +911,7 @@ function reScale( $dimension, $dummy ) {
$new_dimension = $dimension; $new_dimension = $dimension;
for ( $i = 1; $i < func_num_args(); $i++ ) { for ( $i = 1; $i < func_num_args(); $i++ ) {
$scale = func_get_arg( $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); $new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE);
} }
return( $new_dimension ); return( $new_dimension );

View File

@ -49,8 +49,14 @@ function loadLanguage( $prefix="" )
return( false ); return( false );
} }
if ( $langFile = loadLanguage() ) if ( $langFile = loadLanguage() ) {
require_once( $langFile ); 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 ) ); $result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) );
} catch(PDOException $ex) { } catch(PDOException $ex) {
$this->databaseLevel = self::NOLOG; $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 // This has to be last as trigger_error can be fatal

View File

@ -205,7 +205,16 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL; isset($request) || $request = NULL;
isset($action) || $action = 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' ); require_once( 'includes/csrf/csrf-magic.php' );
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); #Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check(); csrf_check();
@ -216,12 +225,15 @@ 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 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) ) { if ( ZM_OPT_USE_AUTH and !isset($user) ) {
Logger::Debug("Redirecting to login" ); Logger::Debug('Redirecting to login');
$view = 'login'; $view = 'login';
$request = null; $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 ) { if ( $redirect ) {
header('Location: '.$redirect); header('Location: '.$redirect);
return; return;

View File

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

View File

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

View File

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

View File

@ -80,6 +80,8 @@ $SLANG = array(
'Actual' => 'Original', 'Actual' => 'Original',
'AddNewControl' => 'Neues Steuerelement hinzufügen', 'AddNewControl' => 'Neues Steuerelement hinzufügen',
'AddNewMonitor' => 'Neuer Monitor', 'AddNewMonitor' => 'Neuer Monitor',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Neuer Benutzer', 'AddNewUser' => 'Neuer Benutzer',
'AddNewZone' => 'Neue Zone', 'AddNewZone' => 'Neue Zone',
'Alarm' => 'Alarm', 'Alarm' => 'Alarm',
@ -107,22 +109,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Archivstatus', 'AttrArchiveStatus' => 'Archivstatus',
'AttrAvgScore' => 'Mittlere Wertung', 'AttrAvgScore' => 'Mittlere Wertung',
'AttrCause' => 'Grund', 'AttrCause' => 'Grund',
'AttrDate' => 'Datum',
'AttrDateTime' => 'Datum/Zeit',
'AttrDiskBlocks' => 'Disk-Blöcke', 'AttrDiskBlocks' => 'Disk-Blöcke',
'AttrDiskPercent' => 'Disk-Prozent', 'AttrDiskPercent' => 'Disk-Prozent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Dauer', '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', 'AttrFrames' => 'Bilder',
'AttrId' => 'ID', 'AttrId' => 'ID',
'AttrMaxScore' => 'Maximale Wertung', 'AttrMaxScore' => 'Maximale Wertung',
'AttrMonitorId' => 'Monitor-ID', 'AttrMonitorId' => 'Monitor-ID',
'AttrMonitorName' => 'Monitorname', 'AttrMonitorName' => 'Monitorname',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Name', 'AttrName' => 'Name',
'AttrNotes' => 'Bemerkungen', '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', 'AttrSystemLoad' => 'Systemlast',
'AttrTime' => 'Zeit',
'AttrTotalScore' => 'Gesamtwertung', 'AttrTotalScore' => 'Gesamtwertung',
'AttrWeekday' => 'Wochentag',
'Auto' => 'Auto', 'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung', 'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung',
'Available' => 'Verfügbar', // Added - 2009-03-31 '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', 'BadRefBlendPerc' => 'Der Referenz-Blenden-Prozentwert muss ganzzahlig 0 oder größer sein',
'BadSectionLength' => 'Die Bereichslänge 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', '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', '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', 'BadWarmupCount' => 'Die Anzahl der Vorwärmbilder muss ganzzahlig 0 oder größer sein',
'BadWebColour' => 'Die Webfarbe muss auf einen gültigen Farbwert eingestellt 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', 'BadWidth' => 'Die Breite muss auf einen gültigen Wert eingestellt sein',
'Bandwidth' => 'Bandbreite', '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 '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 'ChooseLogSelection' => 'Log-Auswahl', // Added - 2011-06-17
'ChoosePreset' => 'Voreinstellung auswählen', 'ChoosePreset' => 'Voreinstellung auswählen',
'Clear' => 'Leeren', // Added - 2011-06-16 'Clear' => 'Leeren', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Schließen', 'Close' => 'Schließen',
'Colour' => 'Farbe', 'Colour' => 'Farbe',
'Command' => 'Kommando', 'Command' => 'Kommando',
'Component' => 'Komponente', // Added - 2011-06-16 'Component' => 'Komponente', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Konfig.', 'Config' => 'Konfig.',
'ConfiguredFor' => 'Konfiguriert für', 'ConfiguredFor' => 'Konfiguriert für',
'ConfirmDeleteEvents' => 'Sind Sie sicher, dass Sie die ausgewählten Ereignisse löschen wollen?', '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.', 'DonateRemindWeek' => 'Noch nicht, erinnere mich in einer Woche noch mal.',
'DonateYes' => 'Ja, ich möchte jetzt spenden.', 'DonateYes' => 'Ja, ich möchte jetzt spenden.',
'Download' => 'Download', 'Download' => 'Download',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31 'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31
'Duration' => 'Dauer', 'Duration' => 'Dauer',
'Edit' => 'Bearbeiten', 'Edit' => 'Bearbeiten',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'E-Mail', 'Email' => 'E-Mail',
'EnableAlarms' => 'Alarme aktivieren', 'EnableAlarms' => 'Alarme aktivieren',
'Enabled' => 'Aktiviert', 'Enabled' => 'Aktiviert',
@ -301,6 +319,7 @@ $SLANG = array(
'Events' => 'Ereignisse', 'Events' => 'Ereignisse',
'Exclude' => 'Ausschluss;', 'Exclude' => 'Ausschluss;',
'Execute' => 'Ausführen', 'Execute' => 'Ausführen',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exportieren', 'Export' => 'Exportieren',
'ExportDetails' => 'Exportiere Ereignis-Details', 'ExportDetails' => 'Exportiere Ereignis-Details',
'ExportFailed' => 'Exportieren fehlgeschlagen', 'ExportFailed' => 'Exportieren fehlgeschlagen',
@ -330,8 +349,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Ausführen bei allen Treffern', 'FilterExecuteEvents' => 'Ausführen bei allen Treffern',
'FilterLog' => 'Log filtern', // Added - 2015-04-18 'FilterLog' => 'Log filtern', // Added - 2015-04-18
'FilterMessageEvents' => 'Detaillierte Nachricht zu allen Treffern', 'FilterMessageEvents' => 'Detaillierte Nachricht zu allen Treffern',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter-Pixel', 'FilterPx' => 'Filter-Pixel',
'FilterUnset' => 'Sie müssen eine Breite und Höhe für das Filter angeben', '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', 'FilterUploadEvents' => 'Hochladen aller Treffer',
'FilterVideoEvents' => 'Video für alle Treffer erstellen', 'FilterVideoEvents' => 'Video für alle Treffer erstellen',
'Filters' => 'Filter', 'Filters' => 'Filter',
@ -356,6 +377,7 @@ $SLANG = array(
'Function' => 'Funktion', 'Function' => 'Funktion',
'Gain' => 'Verstärkung', 'Gain' => 'Verstärkung',
'General' => 'Allgemeines', 'General' => 'Allgemeines',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Erzeuge Video', 'GenerateVideo' => 'Erzeuge Video',
'GeneratingVideo' => 'Erzeuge Video...', 'GeneratingVideo' => 'Erzeuge Video...',
'GoToZoneMinder' => 'Besuche ZoneMinder.com', 'GoToZoneMinder' => 'Besuche ZoneMinder.com',
@ -376,6 +398,7 @@ $SLANG = array(
'High' => 'hohe', 'High' => 'hohe',
'HighBW' => 'Hohe B/W', 'HighBW' => 'Hohe B/W',
'Home' => 'Home', 'Home' => 'Home',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Stunde', 'Hour' => 'Stunde',
'Hue' => 'Farbton', 'Hue' => 'Farbton',
'Id' => 'ID', 'Id' => 'ID',
@ -400,6 +423,7 @@ $SLANG = array(
'Line' => 'Zeile', // Added - 2011-06-16 'Line' => 'Zeile', // Added - 2011-06-16
'LinkedMonitors' => 'Verbundene Monitore', 'LinkedMonitors' => 'Verbundene Monitore',
'List' => 'Liste', 'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Last', 'Load' => 'Last',
'Local' => 'Lokal', 'Local' => 'Lokal',
'Log' => 'Log', // Added - 2011-06-16 '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 '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', 'Monitors' => 'Monitore',
'Montage' => 'Montage', 'Montage' => 'Montage',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Monat', 'Month' => 'Monat',
'More' => 'Mehr', // Added - 2011-06-16 'More' => 'Mehr', // Added - 2011-06-16
'MotionFrameSkip' => 'Motion Frame Skip', 'MotionFrameSkip' => 'Motion Frame Skip',
@ -512,6 +537,7 @@ $SLANG = array(
'Next' => 'Nächstes', 'Next' => 'Nächstes',
'No' => 'Nein', 'No' => 'Nein',
'NoDetectedCameras' => 'Keine Kameras erkannt', // Added - 2009-03-31 'NoDetectedCameras' => 'Keine Kameras erkannt', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'Es gibt keine Aufnahmen von diesem Ereignis.', 'NoFramesRecorded' => 'Es gibt keine Aufnahmen von diesem Ereignis.',
'NoGroup' => 'Keine Gruppe', 'NoGroup' => 'Keine Gruppe',
'NoSavedFilters' => 'Keine gespeicherten Filter', 'NoSavedFilters' => 'Keine gespeicherten Filter',
@ -530,6 +556,8 @@ $SLANG = array(
'OpGt' => 'groesser als', 'OpGt' => 'groesser als',
'OpGtEq' => 'groesser oder gleich wie', 'OpGtEq' => 'groesser oder gleich wie',
'OpIn' => 'in Satz', 'OpIn' => 'in Satz',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'kleiner als', 'OpLt' => 'kleiner als',
'OpLtEq' => 'kleiner oder gleich wie', 'OpLtEq' => 'kleiner oder gleich wie',
'OpMatches' => 'zutreffend', 'OpMatches' => 'zutreffend',
@ -539,6 +567,7 @@ $SLANG = array(
'Open' => 'öffnen', 'Open' => 'öffnen',
'OptionHelp' => 'Hilfe', 'OptionHelp' => 'Hilfe',
'OptionRestartWarning' => 'Veränderungen werden erst nach einem Neustart des Programms aktiv.\nFür eine sofortige änderung starten Sie das Programm bitte neu.', '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', 'Options' => 'Optionen',
'OrEnterNewName' => 'oder neuen Namen eingeben', 'OrEnterNewName' => 'oder neuen Namen eingeben',
'Order' => 'Reihenfolge', '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 '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 'Progress' => 'Fortschritt', // Added - 2015-04-18
'Protocol' => 'Protokoll', 'Protocol' => 'Protokoll',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Abspielgeschwindigkeit', 'Rate' => 'Abspielgeschwindigkeit',
'Real' => 'Real', 'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Aufnahme', 'Record' => 'Aufnahme',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Referenz-Bildblende', 'RefImageBlendPct' => 'Referenz-Bildblende',
'Refresh' => 'Aktualisieren', 'Refresh' => 'Aktualisieren',
'Remote' => 'Remote', 'Remote' => 'Remote',
@ -594,6 +627,7 @@ $SLANG = array(
'ReplayAll' => 'Alle Ereignisse', 'ReplayAll' => 'Alle Ereignisse',
'ReplayGapless' => 'Lückenlose Ereignisse', 'ReplayGapless' => 'Lückenlose Ereignisse',
'ReplaySingle' => 'Einzelereignis', 'ReplaySingle' => 'Einzelereignis',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Zurücksetzen', 'Reset' => 'Zurücksetzen',
'ResetEventCounts' => 'Lösche Ereignispunktzahl', 'ResetEventCounts' => 'Lösche Ereignispunktzahl',
'Restart' => 'Neustart', 'Restart' => 'Neustart',
@ -612,6 +646,7 @@ $SLANG = array(
'Save' => 'Speichern', 'Save' => 'Speichern',
'SaveAs' => 'Speichere als', 'SaveAs' => 'Speichere als',
'SaveFilter' => 'Speichere Filter', 'SaveFilter' => 'Speichere Filter',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Skalierung', 'Scale' => 'Skalierung',
'Score' => 'Wertung', 'Score' => 'Wertung',
'Secs' => 'Sekunden', 'Secs' => 'Sekunden',
@ -628,6 +663,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Zeige Filterfenster', 'ShowFilterWindow' => 'Zeige Filterfenster',
'ShowTimeline' => 'Zeige Zeitstrahl', 'ShowTimeline' => 'Zeige Zeitstrahl',
'SignalCheckColour' => 'Farbe des Signalchecks', 'SignalCheckColour' => 'Farbe des Signalchecks',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Größe', 'Size' => 'Größe',
'SkinDescription' => 'Wähle den standard Skin für diesen Computer.', // Added - 2011-01-30 'SkinDescription' => 'Wähle den standard Skin für diesen Computer.', // Added - 2011-01-30
'Sleep' => 'Schlaf', 'Sleep' => 'Schlaf',
@ -647,6 +683,10 @@ $SLANG = array(
'State' => 'Status', 'State' => 'Status',
'Stats' => 'Statistik', 'Stats' => 'Statistik',
'Status' => 'Status', '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', 'Step' => 'Stufe',
'StepBack' => 'Einen Schritt rückwärts', 'StepBack' => 'Einen Schritt rückwärts',
'StepForward' => 'Einen Schritt vorwärts', 'StepForward' => 'Einen Schritt vorwärts',
@ -657,6 +697,8 @@ $SLANG = array(
'Stills' => 'Standbilder', 'Stills' => 'Standbilder',
'Stop' => 'Stop', 'Stop' => 'Stop',
'Stopped' => 'Gestoppt', 'Stopped' => 'Gestoppt',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream', 'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream-Wiedergabe-Bildpuffer', 'StreamReplayBuffer' => 'Stream-Wiedergabe-Bildpuffer',
'Submit' => 'Absenden', '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. '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', 'Timestamp' => 'Zeitstempel',
'TimestampLabelFormat' => 'Format des Zeitstempels', 'TimestampLabelFormat' => 'Format des Zeitstempels',
'TimestampLabelSize' => 'Schriftgröße',
'TimestampLabelX' => 'Zeitstempel-X', 'TimestampLabelX' => 'Zeitstempel-X',
'TimestampLabelY' => 'Zeitstempel-Y', 'TimestampLabelY' => 'Zeitstempel-Y',
'TimestampLabelSize' => 'Schriftgröße',
'Today' => 'Heute', 'Today' => 'Heute',
'Tools' => 'Werkzeuge', 'Tools' => 'Werkzeuge',
'Total' => 'Insgesamt', // Added - 2011-06-16 'Total' => 'Insgesamt', // Added - 2011-06-16
@ -723,6 +765,7 @@ $SLANG = array(
'VideoGenParms' => 'Parameter der Videoerzeugung', 'VideoGenParms' => 'Parameter der Videoerzeugung',
'VideoGenSucceeded' => 'Videoerzeugung erfolgreich!', 'VideoGenSucceeded' => 'Videoerzeugung erfolgreich!',
'VideoSize' => 'Videogröße', 'VideoSize' => 'Videogröße',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Ansicht', 'View' => 'Ansicht',
'ViewAll' => 'Alles ansehen', 'ViewAll' => 'Alles ansehen',
'ViewEvent' => 'Zeige Ereignis', 'ViewEvent' => 'Zeige Ereignis',
@ -732,6 +775,7 @@ $SLANG = array(
'Watch' => 'Beobachte', 'Watch' => 'Beobachte',
'Web' => 'Web', 'Web' => 'Web',
'WebColour' => 'Webfarbe', 'WebColour' => 'Webfarbe',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Woche', 'Week' => 'Woche',
'White' => 'Weiß', 'White' => 'Weiß',
'WhiteBalance' => 'Weiß-Abgleich', '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 // Simple String Replacements
$SLANG = array( $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', '24BitColour' => '24 bit farve',
'32BitColour' => '32 bit farve', '32BitColour' => '32 bit farve',
'8BitGrey' => '8 bit gråskala', '8BitGrey' => '8 bit gråskala',
@ -89,6 +80,7 @@ $SLANG = array(
'AddNewControl' => 'Tilføj Ny Kontrol', 'AddNewControl' => 'Tilføj Ny Kontrol',
'AddNewMonitor' => 'Tilføj Ny Monitor', 'AddNewMonitor' => 'Tilføj Ny Monitor',
'AddNewServer' => 'Tilføj Ny Server', 'AddNewServer' => 'Tilføj Ny Server',
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Tilføj Ny Bruger', 'AddNewUser' => 'Tilføj Ny Bruger',
'AddNewZone' => 'Tilføj Ny Zone', 'AddNewZone' => 'Tilføj Ny Zone',
'Alarm' => 'Alarm', 'Alarm' => 'Alarm',
@ -98,8 +90,8 @@ $SLANG = array(
'AlarmLimits' => 'Alarm Grænser', 'AlarmLimits' => 'Alarm Grænser',
'AlarmMaximumFPS' => 'Alarm Maksimum FPS', 'AlarmMaximumFPS' => 'Alarm Maksimum FPS',
'AlarmPx' => 'Alarm Px', 'AlarmPx' => 'Alarm Px',
'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %',
'AlarmRGBUnset' => 'Du skal vælge en alarm RGB farve', 'AlarmRGBUnset' => 'Du skal vælge en alarm RGB farve',
'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %',
'Alert' => 'Advarsel', 'Alert' => 'Advarsel',
'All' => 'Alle', 'All' => 'Alle',
'AnalysisFPS' => 'Analyse FPS', 'AnalysisFPS' => 'Analyse FPS',
@ -107,37 +99,45 @@ $SLANG = array(
'Apply' => 'Udfør', 'Apply' => 'Udfør',
'ApplyingStateChange' => 'Udfører tilstandsændring', 'ApplyingStateChange' => 'Udfører tilstandsændring',
'ArchArchived' => 'Kun arkiverede', 'ArchArchived' => 'Kun arkiverede',
'ArchUnarchived' => 'Kun ikke-arkiverede',
'Archive' => 'Arkivér', 'Archive' => 'Arkivér',
'Archived' => 'Arkiverede', 'Archived' => 'Arkiverede',
'ArchUnarchived' => 'Kun ikke-arkiverede',
'Area' => 'Område', 'Area' => 'Område',
'AreaUnits' => 'Område (px/%)', 'AreaUnits' => 'Område (px/%)',
'AttrAlarmFrames' => 'Alarm Rammer', 'AttrAlarmFrames' => 'Alarm Rammer',
'AttrArchiveStatus' => 'Arkiverings Status', 'AttrArchiveStatus' => 'Arkiverings Status',
'AttrAvgScore' => 'Middel Score', 'AttrAvgScore' => 'Middel Score',
'AttrCause' => 'Årsag', 'AttrCause' => 'Årsag',
'AttrDate' => 'Dato',
'AttrDateTime' => 'Dato/Tid',
'AttrDiskBlocks' => 'Disk Blokke', 'AttrDiskBlocks' => 'Disk Blokke',
'AttrDiskPercent' => 'Disk Procent', 'AttrDiskPercent' => 'Disk Procent',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Varighed', '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', 'AttrFrames' => 'Rammer',
'AttrId' => 'Id', 'AttrId' => 'Id',
'AttrMaxScore' => 'Max. Score', 'AttrMaxScore' => 'Max. Score',
'AttrMonitorId' => 'Monitor Id', 'AttrMonitorId' => 'Monitor Id',
'AttrMonitorName' => 'Monitor Navn', 'AttrMonitorName' => 'Monitor Navn',
'AttrServer' => 'Server', 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Navn', 'AttrName' => 'Navn',
'AttrNotes' => 'Noter', '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', 'AttrSystemLoad' => 'System Belastning',
'AttrTime' => 'Tid',
'AttrTotalScore' => 'Total Score', 'AttrTotalScore' => 'Total Score',
'AttrWeekday' => 'Ugedag',
'Auto' => 'Auto', 'Auto' => 'Auto',
'AutoStopTimeout' => 'Auto Stop Timeout', 'AutoStopTimeout' => 'Auto Stop Timeout',
'Available' => 'Tilgængelig', 'Available' => 'Tilgængelig',
'AvgBrScore' => 'Middel<br/>Score', 'AvgBrScore' => 'Middel<br/>Score',
'Available' => 'Tilgængelig',
'Background' => 'Baggrund', 'Background' => 'Baggrund',
'BackgroundFilter' => 'Kør filteret i baggrunden', 'BackgroundFilter' => 'Kør filteret i baggrunden',
'BadAlarmFrameCount' => 'Antal alarm rammer skal være et positivt heltal', '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', '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', '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', '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', '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', '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', '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', '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://', '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', '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', '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', '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', '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', 'BadNameChars' => 'Navne kan kun indeholde alfanumeriske tegn samt mellemrum, bindestreg og understregning',
'BadPalette' => 'Palette skal sættes til en gyldig værdi', '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', 'BadPath' => 'Sti skal sættes til en gyldig værdi',
'BadPort' => 'Port skal sættes til et gyldigt nummer', 'BadPort' => 'Port skal sættes til et gyldigt nummer',
'BadPostEventCount' => 'Antal rammer efter hændelsen skal være et heltal på 0 eller mere', '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', 'BadRefBlendPerc' => 'Reference blandings procentdelen skal være et positivt heltal',
'BadSectionLength' => 'Sektionslængden skal være et heltal på 30 eller mere', 'BadSectionLength' => 'Sektionslængden skal være et heltal på 30 eller mere',
'BadSignalCheckColour' => 'Signal check farve skal være en gyldig RGB farve streng', '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', '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', 'BadWarmupCount' => 'Opvarmnings rammer skal være et heltal på 0 eller mere',
'BadWebColour' => 'Web farve skal være en gyldigt web farve streng', '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', 'BadWidth' => 'Bredde skal sættes til en gyldig værdi',
'Bandwidth' => 'Båndbredde', '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 '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', 'BlobPx' => 'Blob Px',
'Blobs' => 'Blobs',
'BlobSizes' => 'Blob Størrelser', 'BlobSizes' => 'Blob Størrelser',
'Blobs' => 'Blobs',
'Brightness' => 'Lysstyrke', 'Brightness' => 'Lysstyrke',
'Buffer' => 'Buffer', 'Buffer' => 'Buffer',
'Buffers' => 'Buffere', 'Buffers' => 'Buffere',
'CSSDescription' => 'SKift standard css for denne computer',
'CanAutoFocus' => 'Can Auto Focus', 'CanAutoFocus' => 'Can Auto Focus',
'CanAutoGain' => 'Can Auto Gain', 'CanAutoGain' => 'Can Auto Gain',
'CanAutoIris' => 'Can Auto Iris', 'CanAutoIris' => 'Can Auto Iris',
'CanAutoWhite' => 'Can Auto White Bal.', 'CanAutoWhite' => 'Can Auto White Bal.',
'CanAutoZoom' => 'Can Auto Zoom', 'CanAutoZoom' => 'Can Auto Zoom',
'Cancel' => 'Fortryd',
'CancelForcedAlarm' => 'Fortryd Tvungen Alarm',
'CanFocusAbs' => 'Can Focus Absolute',
'CanFocus' => 'Can Focus', 'CanFocus' => 'Can Focus',
'CanFocusAbs' => 'Can Focus Absolute',
'CanFocusCon' => 'Can Focus Continuous', 'CanFocusCon' => 'Can Focus Continuous',
'CanFocusRel' => 'Can Focus Relative', 'CanFocusRel' => 'Can Focus Relative',
'CanGainAbs' => 'Can Gain Absolute',
'CanGain' => 'Can Gain ', 'CanGain' => 'Can Gain ',
'CanGainAbs' => 'Can Gain Absolute',
'CanGainCon' => 'Can Gain Continuous', 'CanGainCon' => 'Can Gain Continuous',
'CanGainRel' => 'Can Gain Relative', 'CanGainRel' => 'Can Gain Relative',
'CanIrisAbs' => 'Can Iris Absolute',
'CanIris' => 'Can Iris', 'CanIris' => 'Can Iris',
'CanIrisAbs' => 'Can Iris Absolute',
'CanIrisCon' => 'Can Iris Continuous', 'CanIrisCon' => 'Can Iris Continuous',
'CanIrisRel' => 'Can Iris Relative', 'CanIrisRel' => 'Can Iris Relative',
'CanMoveAbs' => 'Can Move Absolute',
'CanMove' => 'Can Move', 'CanMove' => 'Can Move',
'CanMoveAbs' => 'Can Move Absolute',
'CanMoveCon' => 'Can Move Continuous', 'CanMoveCon' => 'Can Move Continuous',
'CanMoveDiag' => 'Can Move Diagonally', 'CanMoveDiag' => 'Can Move Diagonally',
'CanMoveMap' => 'Can Move Mapped', 'CanMoveMap' => 'Can Move Mapped',
@ -209,19 +210,21 @@ $SLANG = array(
'CanSleep' => 'Can Sleep', 'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt', 'CanTilt' => 'Can Tilt',
'CanWake' => 'Can Wake', 'CanWake' => 'Can Wake',
'CanWhite' => 'Can White Balance',
'CanWhiteAbs' => 'Can White Bal. Absolute', 'CanWhiteAbs' => 'Can White Bal. Absolute',
'CanWhiteBal' => 'Can White Bal.', 'CanWhiteBal' => 'Can White Bal.',
'CanWhite' => 'Can White Balance',
'CanWhiteCon' => 'Can White Bal. Continuous', 'CanWhiteCon' => 'Can White Bal. Continuous',
'CanWhiteRel' => 'Can White Bal. Relative', 'CanWhiteRel' => 'Can White Bal. Relative',
'CanZoomAbs' => 'Can Zoom Absolute',
'CanZoom' => 'Can Zoom', 'CanZoom' => 'Can Zoom',
'CanZoomAbs' => 'Can Zoom Absolute',
'CanZoomCon' => 'Can Zoom Continuous', 'CanZoomCon' => 'Can Zoom Continuous',
'CanZoomRel' => 'Can Zoom Relative', 'CanZoomRel' => 'Can Zoom Relative',
'Cancel' => 'Fortryd',
'CancelForcedAlarm' => 'Fortryd Tvungen Alarm',
'CaptureHeight' => 'Capture Højde', 'CaptureHeight' => 'Capture Højde',
'CaptureMethod' => 'Capture Metode', 'CaptureMethod' => 'Capture Metode',
'CaptureResolution' => 'Capture Opløsning',
'CapturePalette' => 'Capture Palette', 'CapturePalette' => 'Capture Palette',
'CaptureResolution' => 'Capture Opløsning',
'CaptureWidth' => 'Capture Bredde', 'CaptureWidth' => 'Capture Bredde',
'Cause' => 'Årsag', 'Cause' => 'Årsag',
'CheckMethod' => 'Alarm Check Metode', 'CheckMethod' => 'Alarm Check Metode',
@ -230,10 +233,13 @@ $SLANG = array(
'ChooseLogFormat' => 'Vælg et lognings format', 'ChooseLogFormat' => 'Vælg et lognings format',
'ChooseLogSelection' => 'Vælg et lognings udvælgelse', 'ChooseLogSelection' => 'Vælg et lognings udvælgelse',
'ChoosePreset' => 'Vælg Forudindstilling', 'ChoosePreset' => 'Vælg Forudindstilling',
'Clear' => 'Slet',
'CloneMonitor' => 'Klon Monitor', 'CloneMonitor' => 'Klon Monitor',
'Close' => 'Luk', 'Close' => 'Luk',
'Colour' => 'Farve', 'Colour' => 'Farve',
'Command' => 'Kommando', 'Command' => 'Kommando',
'Component' => 'Komponent',
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Konfigurer', 'Config' => 'Konfigurer',
'ConfiguredFor' => 'Konfigureret for', 'ConfiguredFor' => 'Konfigureret for',
'ConfirmDeleteEvents' => 'Er du sikker på, at du vil slette de markerede hændelser?', '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.', 'ContactAdmin' => 'Venligst kontakt din administrator for detaljer.',
'Continue' => 'Fortsæt', 'Continue' => 'Fortsæt',
'Contrast' => 'Kontrast', 'Contrast' => 'Kontrast',
'Control' => 'Control',
'ControlAddress' => 'Control Address', 'ControlAddress' => 'Control Address',
'ControlCap' => 'Control Capability', 'ControlCap' => 'Control Capability',
'ControlCaps' => 'Control Capabilities', 'ControlCaps' => 'Control Capabilities',
'Control' => 'Control',
'ControlDevice' => 'Control Device', 'ControlDevice' => 'Control Device',
'Controllable' => 'Controllable',
'ControlType' => 'Control Type', 'ControlType' => 'Control Type',
'Controllable' => 'Controllable',
'Current' => 'Nuværende', 'Current' => 'Nuværende',
'Cycle' => 'Cyklisk', 'Cycle' => 'Cyklisk',
'CycleWatch' => 'Cyklisk Overvågning', 'CycleWatch' => 'Cyklisk Overvågning',
'DateTime' => 'Dato/Tid',
'Day' => 'Dag', 'Day' => 'Dag',
'Debug' => 'Fejlfind', 'Debug' => 'Fejlfind',
'DefaultRate' => 'Standard Rate', 'DefaultRate' => 'Standard Rate',
'DefaultScale' => 'Standard Skalering', 'DefaultScale' => 'Standard Skalering',
'DefaultView' => 'Standard Visning', 'DefaultView' => 'Standard Visning',
'Deinterlacing' => 'Deinterlacing', 'Deinterlacing' => 'Deinterlacing',
'RTSPDescribe' => 'Brug RTSP Response Media URL',
'Delay' => 'Forsilkelse', 'Delay' => 'Forsilkelse',
'Delete' => 'Slet',
'DeleteAndNext' => 'Slet &amp; Næste', 'DeleteAndNext' => 'Slet &amp; Næste',
'DeleteAndPrev' => 'Slet &amp; Forrige', 'DeleteAndPrev' => 'Slet &amp; Forrige',
'Delete' => 'Slet',
'DeleteSavedFilter' => 'Slet gemt filter', 'DeleteSavedFilter' => 'Slet gemt filter',
'Description' => 'Beskrivelse', 'Description' => 'Beskrivelse',
'DetectedCameras' => 'Fundne Kameraer', 'DetectedCameras' => 'Fundne Kameraer',
'DetectedProfiles' => 'Fundne Profiler', 'DetectedProfiles' => 'Fundne Profiler',
'Device' => 'Enheds',
'DeviceChannel' => 'Enheds Kanal', 'DeviceChannel' => 'Enheds Kanal',
'DeviceFormat' => 'Enheds Format', 'DeviceFormat' => 'Enheds Format',
'DeviceNumber' => 'Enheds Number', 'DeviceNumber' => 'Enheds Number',
'DevicePath' => 'Sti Til Enhed', 'DevicePath' => 'Sti Til Enhed',
'Device' => 'Enheds',
'Devices' => 'Enheder', 'Devices' => 'Enheder',
'Dimensions' => 'Dimensioner', 'Dimensions' => 'Dimensioner',
'DisableAlarms' => 'Deaktiver Alarmer', 'DisableAlarms' => 'Deaktiver Alarmer',
'Disk' => 'Disk', 'Disk' => 'Disk',
'Display' => 'Display', 'Display' => 'Display',
'Displaying' => 'Displaying', 'Displaying' => 'Displaying',
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Venligst Donér',
'DonateAlready' => 'Nej, jeg har allerede doneret', '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.', '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', 'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag',
'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time', 'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time',
'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned', 'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned',
'DonateRemindNever' => 'Nej, jeg ønsker ikke at donere, påmind ikke igen', 'DonateRemindNever' => 'Nej, jeg ønsker ikke at donere, påmind ikke igen',
'DonateRemindWeek' => 'Ikke endnu, påmind igen on 1 uge', 'DonateRemindWeek' => 'Ikke endnu, påmind igen on 1 uge',
'DonateYes' => 'Ja, jeg vil gerne donere nu', 'DonateYes' => 'Ja, jeg vil gerne donere nu',
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Download' => 'Download', 'Download' => 'Download',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Dupliket Monitor Navn', 'DuplicateMonitorName' => 'Dupliket Monitor Navn',
'Duration' => 'Varighed', 'Duration' => 'Varighed',
'Edit' => 'Ret', 'Edit' => 'Ret',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email', 'Email' => 'Email',
'EnableAlarms' => 'Aktivér Alarmer', 'EnableAlarms' => 'Aktivér Alarmer',
'Enabled' => 'Virksom', 'Enabled' => 'Virksom',
'EnterNewFilterName' => 'Indtast nyt filternavn', 'EnterNewFilterName' => 'Indtast nyt filternavn',
'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer',
'Error' => 'Fejl', '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', 'ErrorValidValue' => 'Fejl, venligst check at alle parametre har en gyldig værdi',
'Etc' => 'etc', 'Etc' => 'etc',
'Event' => 'Hændelse', 'Event' => 'Hændelse',
@ -310,9 +318,9 @@ $SLANG = array(
'Events' => 'Hændelser', 'Events' => 'Hændelser',
'Exclude' => 'Ekskluder', 'Exclude' => 'Ekskluder',
'Execute' => 'Udfør', 'Execute' => 'Udfør',
'ExportDetails' => 'Exporter Hændelses Detaljer',
'Exif' => 'Indlejre EXIF data i billede', 'Exif' => 'Indlejre EXIF data i billede',
'Export' => 'Exporter', 'Export' => 'Exporter',
'ExportDetails' => 'Exporter Hændelses Detaljer',
'ExportFailed' => 'Export Mislykkedes', 'ExportFailed' => 'Export Mislykkedes',
'ExportFormat' => 'Export Fil Format', 'ExportFormat' => 'Export Fil Format',
'ExportFormatTar' => 'Tar', 'ExportFormatTar' => 'Tar',
@ -320,53 +328,55 @@ $SLANG = array(
'ExportFrames' => 'Exporter Ramme Detaljer', 'ExportFrames' => 'Exporter Ramme Detaljer',
'ExportImageFiles' => 'Exporter billed filer', 'ExportImageFiles' => 'Exporter billed filer',
'ExportLog' => 'Export Log', 'ExportLog' => 'Export Log',
'Exporting' => 'Exporterer',
'ExportMiscFiles' => 'Exporter Andre Filer (hvis tilstede)', 'ExportMiscFiles' => 'Exporter Andre Filer (hvis tilstede)',
'ExportOptions' => 'Export Indstillinger', 'ExportOptions' => 'Export Indstillinger',
'ExportSucceeded' => 'Export Lykkedes', 'ExportSucceeded' => 'Export Lykkedes',
'ExportVideoFiles' => 'Exporter Video Filer (hvis tilstede)', 'ExportVideoFiles' => 'Exporter Video Filer (hvis tilstede)',
'Exporting' => 'Exporterer',
'FPS' => 'fps',
'FPSReportInterval' => 'FPS Rapport Interval',
'FTP' => 'FTP',
'Far' => 'Fjern', 'Far' => 'Fjern',
'FastForward' => 'Hurtigt Frem', 'FastForward' => 'Hurtigt Frem',
'Feed' => 'Feed', 'Feed' => 'Feed',
'Ffmpeg' => 'Ffmpeg', 'Ffmpeg' => 'Ffmpeg',
'File' => 'Fil', 'File' => 'Fil',
'Filter' => 'Filter',
'FilterArchiveEvents' => 'Arkiver alle matchende', 'FilterArchiveEvents' => 'Arkiver alle matchende',
'FilterDeleteEvents' => 'Slet alle matchende', 'FilterDeleteEvents' => 'Slet alle matchende',
'FilterEmailEvents' => 'Email detaljer for alle matchende', 'FilterEmailEvents' => 'Email detaljer for alle matchende',
'FilterExecuteEvents' => 'Udfør kommando for alle matchende', 'FilterExecuteEvents' => 'Udfør kommando for alle matchende',
'FilterLog' => 'Filter log', 'FilterLog' => 'Filter log',
'FilterMessageEvents' => 'Meddel detaljer for alle matchende', 'FilterMessageEvents' => 'Meddel detaljer for alle matchende',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filter Px', 'FilterPx' => 'Filter Px',
'Filter' => 'Filter',
'Filters' => 'Filtre',
'FilterUnset' => 'Du skal angive filter bredde og højde', 'FilterUnset' => 'Du skal angive filter bredde og højde',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Upload alle match', 'FilterUploadEvents' => 'Upload alle match',
'FilterVideoEvents' => 'Opret video for alle match', 'FilterVideoEvents' => 'Opret video for alle match',
'Filters' => 'Filtre',
'First' => 'Første', 'First' => 'Første',
'FlippedHori' => 'Spejlet Horizontalt', 'FlippedHori' => 'Spejlet Horizontalt',
'FlippedVert' => 'Spejlet Vertikalt', '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. 'FnMocord' => 'Mocord', // Added 2013.08.16.
'FnModect' => 'Modect', // Added 2013.08.16.
'FnMonitor' => 'Monitor', // Added 2013.08.16.
'FnNodect' => 'Nodect', // Added 2013.08.16. 'FnNodect' => 'Nodect', // Added 2013.08.16.
'FnNone' => 'None', // Added 2013.08.16.
'FnRecord' => 'Record', // Added 2013.08.16.
'Focus' => 'Focus', 'Focus' => 'Focus',
'ForceAlarm' => 'Force Alarm', 'ForceAlarm' => 'Force Alarm',
'Format' => 'Format', 'Format' => 'Format',
'FPS' => 'fps',
'FPSReportInterval' => 'FPS Rapport Interval',
'Frame' => 'Ramme', 'Frame' => 'Ramme',
'FrameId' => 'Ramme Id', 'FrameId' => 'Ramme Id',
'FrameRate' => 'Billedhastighed', 'FrameRate' => 'Billedhastighed',
'Frames' => 'Rammer',
'FrameSkip' => 'Spring over antal rammer', 'FrameSkip' => 'Spring over antal rammer',
'MotionFrameSkip' => 'Spring over antal bevægelsesrammer', 'Frames' => 'Rammer',
'FTP' => 'FTP',
'Func' => 'Funk', 'Func' => 'Funk',
'Function' => 'Funktion', 'Function' => 'Funktion',
'Gain' => 'Gain', 'Gain' => 'Gain',
'General' => 'Generelt', 'General' => 'Generelt',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Generer Video', 'GenerateVideo' => 'Generer Video',
'GeneratingVideo' => 'Genererer Video', 'GeneratingVideo' => 'Genererer Video',
'GoToZoneMinder' => 'Gå til ZoneMinder.com', 'GoToZoneMinder' => 'Gå til ZoneMinder.com',
@ -384,8 +394,8 @@ $SLANG = array(
'HasTurboTilt' => 'Has Turbo Tilt', 'HasTurboTilt' => 'Has Turbo Tilt',
'HasWhiteSpeed' => 'Has White Bal. Speed', 'HasWhiteSpeed' => 'Has White Bal. Speed',
'HasZoomSpeed' => 'Has Zoom Speed', 'HasZoomSpeed' => 'Has Zoom Speed',
'HighBW' => 'High&nbsp;B/W',
'High' => 'Høj', 'High' => 'Høj',
'HighBW' => 'High&nbsp;B/W',
'Home' => 'Hjemme', 'Home' => 'Hjemme',
'Hostname' => 'Hostname', 'Hostname' => 'Hostname',
'Hour' => 'Time', 'Hour' => 'Time',
@ -393,11 +403,11 @@ $SLANG = array(
'Id' => 'Id', 'Id' => 'Id',
'Idle' => 'Afventende', 'Idle' => 'Afventende',
'Ignore' => 'Ignorer', 'Ignore' => 'Ignorer',
'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)',
'Image' => 'Billede', 'Image' => 'Billede',
'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)',
'Images' => 'Billeder', 'Images' => 'Billeder',
'Include' => 'Inkluder',
'In' => 'I', 'In' => 'I',
'Include' => 'Inkluder',
'Inverted' => 'Inverteret', 'Inverted' => 'Inverteret',
'Iris' => 'Blænde', 'Iris' => 'Blænde',
'KeyString' => 'Nøgle Streng', 'KeyString' => 'Nøgle Streng',
@ -405,26 +415,30 @@ $SLANG = array(
'Language' => 'Sprog', 'Language' => 'Sprog',
'Last' => 'Sidste', 'Last' => 'Sidste',
'Layout' => 'Layout', 'Layout' => 'Layout',
'Level' => 'Niveau',
'Libvlc' => 'Libvlc', 'Libvlc' => 'Libvlc',
'LimitResultsPost' => 'resultater', // This is used at the end of the phrase 'Limit to first N results only' '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' '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', 'LinkedMonitors' => 'Sammenkædede Monitorer',
'List' => 'Liste', 'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Belastning', 'Load' => 'Belastning',
'Local' => 'Lokal', 'Local' => 'Lokal',
'Log' => 'Log', 'Log' => 'Log',
'Logs' => 'Logs',
'Logging' => 'Logning',
'LoggedInAs' => 'Logget ind som', 'LoggedInAs' => 'Logget ind som',
'Logging' => 'Logning',
'LoggingIn' => 'Logger ind', 'LoggingIn' => 'Logger ind',
'Login' => 'Logind', 'Login' => 'Logind',
'Logout' => 'Logud', 'Logout' => 'Logud',
'LowBW' => 'Lav&nbsp;B/W', 'Logs' => 'Logs',
'Low' => 'Lav', 'Low' => 'Lav',
'LowBW' => 'Lav&nbsp;B/W',
'Main' => 'Hoved', 'Main' => 'Hoved',
'Man' => 'Man', 'Man' => 'Man',
'Manual' => 'Manuel', 'Manual' => 'Manuel',
'Mark' => 'Markér', 'Mark' => 'Markér',
'Max' => 'Max',
'MaxBandwidth' => 'Max Båndbredde', 'MaxBandwidth' => 'Max Båndbredde',
'MaxBrScore' => 'Max.<br/>Score', 'MaxBrScore' => 'Max.<br/>Score',
'MaxFocusRange' => 'Max Focus Range', 'MaxFocusRange' => 'Max Focus Range',
@ -433,11 +447,9 @@ $SLANG = array(
'MaxGainRange' => 'Max Gain Range', 'MaxGainRange' => 'Max Gain Range',
'MaxGainSpeed' => 'Max Gain Speed', 'MaxGainSpeed' => 'Max Gain Speed',
'MaxGainStep' => 'Max Gain Step', 'MaxGainStep' => 'Max Gain Step',
'MaximumFPS' => 'Maximum FPS',
'MaxIrisRange' => 'Max Iris Range', 'MaxIrisRange' => 'Max Iris Range',
'MaxIrisSpeed' => 'Max Iris Speed', 'MaxIrisSpeed' => 'Max Iris Speed',
'MaxIrisStep' => 'Max Iris Step', 'MaxIrisStep' => 'Max Iris Step',
'Max' => 'Max',
'MaxPanRange' => 'Max Pan Range', 'MaxPanRange' => 'Max Pan Range',
'MaxPanSpeed' => 'Max Pan Speed', 'MaxPanSpeed' => 'Max Pan Speed',
'MaxPanStep' => 'Max Pan Step', 'MaxPanStep' => 'Max Pan Step',
@ -450,8 +462,10 @@ $SLANG = array(
'MaxZoomRange' => 'Max Zoom Range', 'MaxZoomRange' => 'Max Zoom Range',
'MaxZoomSpeed' => 'Max Zoom Speed', 'MaxZoomSpeed' => 'Max Zoom Speed',
'MaxZoomStep' => 'Max Zoom Step', 'MaxZoomStep' => 'Max Zoom Step',
'MediumBW' => 'Medium&nbsp;B/W', 'MaximumFPS' => 'Maximum FPS',
'Medium' => 'Medium', 'Medium' => 'Medium',
'MediumBW' => 'Medium&nbsp;B/W',
'Message' => 'Meddelelse',
'MinAlarmAreaLtMax' => 'Minimum alarm område skal være mindre end maksimum', 'MinAlarmAreaLtMax' => 'Minimum alarm område skal være mindre end maksimum',
'MinAlarmAreaUnset' => 'Du skal angive det minimale antal alarm pixels', 'MinAlarmAreaUnset' => 'Du skal angive det minimale antal alarm pixels',
'MinBlobAreaLtMax' => 'Minimum blob område skal være mindre end maksimum', 'MinBlobAreaLtMax' => 'Minimum blob område skal være mindre end maksimum',
@ -487,22 +501,24 @@ $SLANG = array(
'MinZoomStep' => 'Min Zoom Step', 'MinZoomStep' => 'Min Zoom Step',
'Misc' => 'Diverse', 'Misc' => 'Diverse',
'Mode' => 'Mode', 'Mode' => 'Mode',
'MonitorIds' => 'Monitor&nbsp;Ids',
'Monitor' => 'Monitor', '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', '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', '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', 'Monitors' => 'Monitorer',
'Montage' => 'Montage', 'Montage' => 'Montage',
'MontageReview' => 'Montage Review', 'MontageReview' => 'Montage Review',
'Month' => 'Måned', 'Month' => 'Måned',
'More' => 'Mere',
'MotionFrameSkip' => 'Spring over antal bevægelsesrammer',
'Move' => 'Bevæg', 'Move' => 'Bevæg',
'MtgDefault' => 'Standard', // Added 2013.08.15.
'Mtg2widgrd' => '2-bred gitter', // Added 2013.08.15. 'Mtg2widgrd' => '2-bred gitter', // Added 2013.08.15.
'Mtg3widgrd' => '3-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. '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', 'MustBeGe' => 'Skal være større end eller lig med',
'MustBeLe' => 'Skal være mindre end eller lig med', 'MustBeLe' => 'Skal være mindre end eller lig med',
'MustConfirmPassword' => 'Du skal bekræfte adgangskoden', 'MustConfirmPassword' => 'Du skal bekræfte adgangskoden',
@ -511,53 +527,55 @@ $SLANG = array(
'Name' => 'Navn', 'Name' => 'Navn',
'Near' => 'Nær', 'Near' => 'Nær',
'Network' => 'Netværk', 'Network' => 'Netværk',
'New' => 'Ny',
'NewGroup' => 'Ny Gruppe', 'NewGroup' => 'Ny Gruppe',
'NewLabel' => 'Ny Mærkat', 'NewLabel' => 'Ny Mærkat',
'New' => 'Ny',
'NewPassword' => 'Ny Adgangskode', 'NewPassword' => 'Ny Adgangskode',
'NewState' => 'Ny Tilstand', 'NewState' => 'Ny Tilstand',
'NewUser' => 'Ny bruger', 'NewUser' => 'Ny bruger',
'Next' => 'Næste', 'Next' => 'Næste',
'No' => 'Nej',
'NoDetectedCameras' => 'Ingen Detected Cameras', 'NoDetectedCameras' => 'Ingen Detected Cameras',
'NoDetectedProfiles' => 'Ingen Fundne Profiler', 'NoDetectedProfiles' => 'Ingen Fundne Profiler',
'NoFramesRecorded' => 'Der er ingen billeder optaget for denne hændelse', 'NoFramesRecorded' => 'Der er ingen billeder optaget for denne hændelse',
'NoGroup' => 'Ingen gruppe', 'NoGroup' => 'Ingen gruppe',
'NoneAvailable' => 'Ingen tilgængelig',
'None' => 'Ingen',
'No' => 'Nej',
'Normal' => 'Normalt',
'NoSavedFilters' => 'IngenGemteFiltre', 'NoSavedFilters' => 'IngenGemteFiltre',
'NoStatisticsRecorded' => 'Der er ingen statistik noteret for denne hændelse/ramme', 'NoStatisticsRecorded' => 'Der er ingen statistik noteret for denne hændelse/ramme',
'None' => 'Ingen',
'NoneAvailable' => 'Ingen tilgængelig',
'Normal' => 'Normalt',
'Notes' => 'Noter', 'Notes' => 'Noter',
'NumPresets' => 'Num Forudinst.', 'NumPresets' => 'Num Forudinst.',
'Off' => 'Fra', 'Off' => 'Fra',
'On' => 'Til', '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', '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/>', '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', 'OpEq' => 'lig med',
'OpGtEq' => 'større end eller lig med',
'OpGt' => 'større end', 'OpGt' => 'større end',
'OpGtEq' => 'større end eller lig med',
'OpIn' => 'indeholdt i', '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', 'OpLt' => 'mindre end',
'OpLtEq' => 'mindre end eller lig med',
'OpMatches' => 'matcher', 'OpMatches' => 'matcher',
'OpNe' => 'ikke lig med', 'OpNe' => 'ikke lig med',
'OpNotIn' => 'ikke indeholdt i', 'OpNotIn' => 'ikke indeholdt i',
'OpNotMatches' => 'matcher ikke', 'OpNotMatches' => 'matcher ikke',
'OptionalEncoderParam' => 'Optionelle Encoder Parametre', 'Open' => 'Åben',
'OptionHelp' => 'Indstillinger hjælp', '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.', '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', 'Options' => 'Indstillinger',
'Order' => 'Rækkefølge',
'OrEnterNewName' => 'eller indtast nyt navn', 'OrEnterNewName' => 'eller indtast nyt navn',
'Order' => 'Rækkefølge',
'Orientation' => 'Orientering', 'Orientation' => 'Orientering',
'Out' => 'Ud', 'Out' => 'Ud',
'OverwriteExisting' => 'Overskriv Eksisterende', 'OverwriteExisting' => 'Overskriv Eksisterende',
'Paged' => 'Sidevis', 'Paged' => 'Sidevis',
'PanLeft' => 'Pan Left',
'Pan' => 'Pan', 'Pan' => 'Pan',
'PanLeft' => 'Pan Left',
'PanRight' => 'Pan Right', 'PanRight' => 'Pan Right',
'PanTilt' => 'Pan/Tilt', 'PanTilt' => 'Pan/Tilt',
'Parameter' => 'Parameter', 'Parameter' => 'Parameter',
@ -565,14 +583,15 @@ $SLANG = array(
'PasswordsDifferent' => 'Den nye og den bekræftende adgangskode er forskellige', 'PasswordsDifferent' => 'Den nye og den bekræftende adgangskode er forskellige',
'Paths' => 'Stier', 'Paths' => 'Stier',
'Pause' => 'Pause', 'Pause' => 'Pause',
'PhoneBW' => 'Telefon&nbsp;B/W',
'Phone' => 'Telefon', 'Phone' => 'Telefon',
'PhoneBW' => 'Telefon&nbsp;B/W',
'Pid' => 'PID',
'PixelDiff' => 'Pixel Forskel', 'PixelDiff' => 'Pixel Forskel',
'Pixels' => 'pixels', 'Pixels' => 'pixels',
'PlayAll' => 'Afspil Alle',
'Play' => 'Afspil', 'Play' => 'Afspil',
'Plugins' => 'Plugins', 'PlayAll' => 'Afspil Alle',
'PleaseWait' => 'Vent venligst', 'PleaseWait' => 'Vent venligst',
'Plugins' => 'Plugins',
'Point' => 'Point', 'Point' => 'Point',
'PostEventImageBuffer' => 'Antal Billeder Efter Hændelse', 'PostEventImageBuffer' => 'Antal Billeder Efter Hændelse',
'PreEventImageBuffer' => 'Antal Billeder Før 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/>', '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', 'Progress' => 'Position',
'Protocol' => 'Protokol', 'Protocol' => 'Protokol',
'RTSPDescribe' => 'Brug RTSP Response Media URL',
'RTSPTransport' => 'RTSP Transport Protocol',
'Rate' => 'Rate', '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', 'Real' => 'Naturtro',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP
'Record' => 'Optag', 'Record' => 'Optag',
'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.',
'RefImageBlendPct' => 'Reference Billede Blandings %', 'RefImageBlendPct' => 'Reference Billede Blandings %',
'Refresh' => 'Genindlæs', 'Refresh' => 'Genindlæs',
'Remote' => 'Remote',
'RemoteHostName' => 'Remote Host Name', 'RemoteHostName' => 'Remote Host Name',
'RemoteHostPath' => 'Remote Host Path', 'RemoteHostPath' => 'Remote Host Path',
'RemoteHostSubPath' => 'Remote Host SubPath',
'RemoteHostPort' => 'Remote Host Port', 'RemoteHostPort' => 'Remote Host Port',
'RemoteHostSubPath' => 'Remote Host SubPath',
'RemoteImageColours' => 'Remote Image Colours', 'RemoteImageColours' => 'Remote Image Colours',
'RemoteMethod' => 'Remote Method', 'RemoteMethod' => 'Remote Method',
'RemoteProtocol' => 'Remote Protocol', 'RemoteProtocol' => 'Remote Protocol',
'Remote' => 'Remote',
'Rename' => 'Omdøb', 'Rename' => 'Omdøb',
'Replay' => 'Genafspil',
'ReplayAll' => 'Alle Hændelser', 'ReplayAll' => 'Alle Hændelser',
'ReplayGapless' => 'Hændelser uafbrudt', 'ReplayGapless' => 'Hændelser uafbrudt',
'Replay' => 'Genafspil',
'ReplaySingle' => 'Enkelt Hændelse', 'ReplaySingle' => 'Enkelt Hændelse',
'ResetEventCounts' => 'Nulstil Hændelses Tæller', 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Nulstil', 'Reset' => 'Nulstil',
'Restarting' => 'Genstarter', 'ResetEventCounts' => 'Nulstil Hændelses Tæller',
'Restart' => 'Genstart', 'Restart' => 'Genstart',
'Restarting' => 'Genstarter',
'RestrictedCameraIds' => 'Restricted Camera Ids', 'RestrictedCameraIds' => 'Restricted Camera Ids',
'RestrictedMonitors' => 'Restricted Monitors', 'RestrictedMonitors' => 'Restricted Monitors',
'ReturnDelay' => 'Return Delay', 'ReturnDelay' => 'Return Delay',
@ -616,34 +638,33 @@ $SLANG = array(
'Rewind' => 'Hurtigt Tilbage', 'Rewind' => 'Hurtigt Tilbage',
'RotateLeft' => 'Roter til venstrte', 'RotateLeft' => 'Roter til venstrte',
'RotateRight' => 'Roter til højre', 'RotateRight' => 'Roter til højre',
'RTSPTransport' => 'RTSP Transport Protocol',
'RunLocalUpdate' => 'Kør venligst zmupdate.pl for at opdatere', 'RunLocalUpdate' => 'Kør venligst zmupdate.pl for at opdatere',
'RunMode' => 'Driftsmåde', 'RunMode' => 'Driftsmåde',
'Running' => 'Kørende',
'RunState' => 'Drift Tilstand', 'RunState' => 'Drift Tilstand',
'Running' => 'Kørende',
'Save' => 'Gem',
'SaveAs' => 'Gem som', 'SaveAs' => 'Gem som',
'SaveFilter' => 'Gem Filter', 'SaveFilter' => 'Gem Filter',
'SaveJPEGS' => 'Gem JPEGs', 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Save' => 'Gem',
'Scale' => 'Skaler', 'Scale' => 'Skaler',
'Score' => 'Score', 'Score' => 'Score',
'Secs' => 'Sek.', 'Secs' => 'Sek.',
'Sectionlength' => 'Sektions længde', 'Sectionlength' => 'Sektions længde',
'SelectMonitors' => 'Vælg Monitorer',
'Select' => 'Vælg', 'Select' => 'Vælg',
'SelectFormat' => 'Vælg Format', 'SelectFormat' => 'Vælg Format',
'SelectLog' => 'Vælg Log', 'SelectLog' => 'Vælg Log',
'SelectMonitors' => 'Vælg Monitorer',
'SelfIntersecting' => 'Polygonens kanter må ikke krydses', 'SelfIntersecting' => 'Polygonens kanter må ikke krydses',
'Set' => 'Sæt',
'SetNewBandwidth' => 'Vælg ny båndbredde', 'SetNewBandwidth' => 'Vælg ny båndbredde',
'SetPreset' => 'Set Preset', 'SetPreset' => 'Set Preset',
'Set' => 'Sæt',
'Settings' => 'Indstillinger', 'Settings' => 'Indstillinger',
'ShowFilterWindow' => 'Vis Filter Vindue', 'ShowFilterWindow' => 'Vis Filter Vindue',
'ShowTimeline' => 'Vis Tidslinie', 'ShowTimeline' => 'Vis Tidslinie',
'SignalCheckColour' => 'Signal Check Colour', 'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Størrelse', 'Size' => 'Størrelse',
'SkinDescription' => 'SKift standard skin for denne computer', 'SkinDescription' => 'SKift standard skin for denne computer',
'CSSDescription' => 'SKift standard css for denne computer',
'Sleep' => 'Sover', 'Sleep' => 'Sover',
'SortAsc' => 'Voksende', 'SortAsc' => 'Voksende',
'SortBy' => 'Sortér efter', 'SortBy' => 'Sortér efter',
@ -652,46 +673,53 @@ $SLANG = array(
'SourceColours' => 'Kilde Farver', 'SourceColours' => 'Kilde Farver',
'SourcePath' => 'Kilde Sti', 'SourcePath' => 'Kilde Sti',
'SourceType' => 'Kilde Type', 'SourceType' => 'Kilde Type',
'Speed' => 'Speed',
'SpeedHigh' => 'High Speed', 'SpeedHigh' => 'High Speed',
'SpeedLow' => 'Low Speed', 'SpeedLow' => 'Low Speed',
'SpeedMedium' => 'Medium Speed', 'SpeedMedium' => 'Medium Speed',
'Speed' => 'Speed',
'SpeedTurbo' => 'Turbo Speed', 'SpeedTurbo' => 'Turbo Speed',
'Start' => 'Start', 'Start' => 'Start',
'State' => 'Tilstand', 'State' => 'Tilstand',
'Stats' => 'Stats', 'Stats' => 'Stats',
'Status' => 'Status', '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', 'StepBack' => 'Skridt Tilbage',
'StepForward' => 'Skridt Frem', 'StepForward' => 'Skridt Frem',
'StepLarge' => 'Langt Skridt', 'StepLarge' => 'Langt Skridt',
'StepMedium' => 'Medium Skridt', 'StepMedium' => 'Medium Skridt',
'StepNone' => 'Ingen Skridt', 'StepNone' => 'Ingen Skridt',
'StepSmall' => 'Lille Skridt', 'StepSmall' => 'Lille Skridt',
'Step' => 'Skridt',
'Stills' => 'Stilbilleder', 'Stills' => 'Stilbilleder',
'Stopped' => 'Stoppet',
'Stop' => 'Stop', 'Stop' => 'Stop',
'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Stopped' => 'Stoppet',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Stream', 'Stream' => 'Stream',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Submit' => 'Påtryk', 'Submit' => 'Påtryk',
'System' => 'System', 'System' => 'System',
'SystemLog' => 'System Log',
'TargetColorspace' => 'Target colorspace', 'TargetColorspace' => 'Target colorspace',
'Tele' => 'Tele', 'Tele' => 'Tele',
'Thumbnail' => 'Thumbnail', 'Thumbnail' => 'Thumbnail',
'Tilt' => 'Tilt', 'Tilt' => 'Tilt',
'Time' => 'Tidspunkt',
'TimeDelta' => 'Tidsforskel', 'TimeDelta' => 'Tidsforskel',
'TimeStamp' => 'Tids stempel',
'Timeline' => 'Tidslinie', 'Timeline' => 'Tidslinie',
'TimelineTip1' => 'Før musen over grafen for at vise snapshot billede og detaljer om hændelsen.', // Added 2013.08.15. '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. '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. '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. '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', 'TimestampLabelFormat' => 'Tidsstempel Mærkat Format',
'TimestampLabelSize' => 'Font Størrelse',
'TimestampLabelX' => 'Tidsstempel Mærkat X', 'TimestampLabelX' => 'Tidsstempel Mærkat X',
'TimestampLabelY' => 'Tidsstempel Mærkat Y', 'TimestampLabelY' => 'Tidsstempel Mærkat Y',
'TimestampLabelSize' => 'Font Størrelse',
'Timestamp' => 'Tidsstempel',
'TimeStamp' => 'Tids stempel',
'Time' => 'Tidspunkt',
'Today' => 'Idag', 'Today' => 'Idag',
'Tools' => 'Værktøjer', 'Tools' => 'Værktøjer',
'Total' => 'Total', 'Total' => 'Total',
@ -706,25 +734,29 @@ $SLANG = array(
'Undefined' => 'Udefineret', 'Undefined' => 'Udefineret',
'Units' => 'Enheder', 'Units' => 'Enheder',
'Unknown' => 'Ukendt', 'Unknown' => 'Ukendt',
'Update' => 'Opdater',
'UpdateAvailable' => 'En opdatering til ZoneMinder er tilgængelig.', 'UpdateAvailable' => 'En opdatering til ZoneMinder er tilgængelig.',
'UpdateNotNecessary' => 'Ingen opdatering er nødvendig.', 'UpdateNotNecessary' => 'Ingen opdatering er nødvendig.',
'Update' => 'Opdater',
'Upload' => 'Upload',
'Updated' => 'Opdateret', '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' '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' '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', 'Username' => 'Brugernavn',
'Users' => 'Brugere', 'Users' => 'Brugere',
'User' => 'Bruger', 'V4L' => 'V4L',
'V4LCapturesPerFrame' => 'Captures Per Frame',
'V4LMultiBuffer' => 'Multi Buffering',
'Value' => 'Værdi', 'Value' => 'Værdi',
'Version' => 'Version',
'VersionIgnore' => 'Ignorer denne værdi', 'VersionIgnore' => 'Ignorer denne værdi',
'VersionRemindDay' => 'Påmind igen om 1 dag', 'VersionRemindDay' => 'Påmind igen om 1 dag',
'VersionRemindHour' => 'Påmind igen om 1 time', 'VersionRemindHour' => 'Påmind igen om 1 time',
'VersionRemindNever' => 'Påmind ikke om nye versioner', 'VersionRemindNever' => 'Påmind ikke om nye versioner',
'VersionRemindWeek' => 'Påmind igen om 1 uge', 'VersionRemindWeek' => 'Påmind igen om 1 uge',
'Version' => 'Version', 'Video' => 'Video',
'VideoFormat' => 'Video Format', 'VideoFormat' => 'Video Format',
'VideoGenFailed' => 'Video Generering Fejlede!', 'VideoGenFailed' => 'Video Generering Fejlede!',
'VideoGenFiles' => 'Existerende Video Filer', 'VideoGenFiles' => 'Existerende Video Filer',
@ -733,47 +765,44 @@ $SLANG = array(
'VideoGenSucceeded' => 'Video Generering Succeeded!', 'VideoGenSucceeded' => 'Video Generering Succeeded!',
'VideoSize' => 'Video Størrelse', 'VideoSize' => 'Video Størrelse',
'VideoWriter' => 'Video Skriver', 'VideoWriter' => 'Video Skriver',
'Video' => 'Video', 'View' => 'Vis',
'ViewAll' => 'Vis Alle', 'ViewAll' => 'Vis Alle',
'ViewEvent' => 'Vis Hændelse', 'ViewEvent' => 'Vis Hændelse',
'ViewPaged' => 'Vis Sidevis', 'ViewPaged' => 'Vis Sidevis',
'View' => 'Vis',
'V4L' => 'V4L',
'V4LCapturesPerFrame' => 'Captures Per Frame',
'V4LMultiBuffer' => 'Multi Buffering',
'Wake' => 'Vågen', 'Wake' => 'Vågen',
'WarmupFrames' => 'Opvarmningsbilleder', 'WarmupFrames' => 'Opvarmningsbilleder',
'Watch' => 'Ur', 'Watch' => 'Ur',
'WebColour' => 'Web Farve',
'Web' => 'Web', 'Web' => 'Web',
'WebColour' => 'Web Farve',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Uge', 'Week' => 'Uge',
'WhiteBalance' => 'Hvidbalance',
'White' => 'Hvid', 'White' => 'Hvid',
'WhiteBalance' => 'Hvidbalance',
'Wide' => 'Bred', 'Wide' => 'Bred',
'X' => 'X',
'X10' => 'X10',
'X10ActivationString' => 'X10 Activerings Streng', 'X10ActivationString' => 'X10 Activerings Streng',
'X10InputAlarmString' => 'X10 Input Alarm Streng', 'X10InputAlarmString' => 'X10 Input Alarm Streng',
'X10OutputAlarmString' => 'X10 Output Alarm Streng', 'X10OutputAlarmString' => 'X10 Output Alarm Streng',
'X10' => 'X10', 'Y' => 'Y',
'X' => 'X',
'Yes' => 'Ja', 'Yes' => 'Ja',
'YouNoPerms' => 'Du har ikke tilladelse til at tilgå denne ressurse.', 'YouNoPerms' => 'Du har ikke tilladelse til at tilgå denne ressurse.',
'Y' => 'Y', 'Zone' => 'Zone',
'ZoneAlarmColour' => 'Alarm Farve (Rød/Grøn/Blå)', 'ZoneAlarmColour' => 'Alarm Farve (Rød/Grøn/Blå)',
'ZoneArea' => 'Zone Område', 'ZoneArea' => 'Zone Område',
'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer',
'ZoneFilterSize' => 'Filter Bredde/Højde (pixels)', 'ZoneFilterSize' => 'Filter Bredde/Højde (pixels)',
'ZoneMinderLog' => 'ZoneMinder Log',
'ZoneMinMaxAlarmArea' => 'Min/Max Alarmeret Område', 'ZoneMinMaxAlarmArea' => 'Min/Max Alarmeret Område',
'ZoneMinMaxBlobArea' => 'Min/Max Blob Område', 'ZoneMinMaxBlobArea' => 'Min/Max Blob Område',
'ZoneMinMaxBlobs' => 'Min/Max Blobs', 'ZoneMinMaxBlobs' => 'Min/Max Blobs',
'ZoneMinMaxFiltArea' => 'Min/Max Filtreret Område', 'ZoneMinMaxFiltArea' => 'Min/Max Filtreret Område',
'ZoneMinMaxPixelThres' => 'Min/Max Pixel Grænseværdi (0-255)', 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Grænseværdi (0-255)',
'ZoneMinderLog' => 'ZoneMinder Log',
'ZoneOverloadFrames' => 'Antal Rammer At Ignorere Efter Overload', 'ZoneOverloadFrames' => 'Antal Rammer At Ignorere Efter Overload',
'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer',
'Zones' => 'Zoner', 'Zones' => 'Zoner',
'Zone' => 'Zone', 'Zoom' => 'Zoom',
'ZoomIn' => 'Zoom Ind', 'ZoomIn' => 'Zoom Ind',
'ZoomOut' => 'Zoom Ud', 'ZoomOut' => 'Zoom Ud',
'Zoom' => 'Zoom',
); );
// Complex replacements with formatting and/or placements, must be passed through sprintf // Complex replacements with formatting and/or placements, must be passed through sprintf

View File

@ -601,6 +601,18 @@ $SLANG = array(
'Preset' => 'Preset', 'Preset' => 'Preset',
'Presets' => 'Presets', 'Presets' => 'Presets',
'Prev' => 'Prev', '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', 'Probe' => 'Probe',
'ProfileProbe' => 'Stream 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/>', '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/>',
@ -639,10 +651,13 @@ $SLANG = array(
'RotateLeft' => 'Rotate Left', 'RotateLeft' => 'Rotate Left',
'RotateRight' => 'Rotate Right', 'RotateRight' => 'Rotate Right',
'RTSPTransport' => 'RTSP Transport Protocol', 'RTSPTransport' => 'RTSP Transport Protocol',
'RunAudit' => 'Run Audit Process',
'RunLocalUpdate' => 'Please run zmupdate.pl to update', 'RunLocalUpdate' => 'Please run zmupdate.pl to update',
'RunMode' => 'Run Mode', 'RunMode' => 'Run Mode',
'Running' => 'Running', 'Running' => 'Running',
'RunState' => 'Run State', 'RunState' => 'Run State',
'RunStats' => 'Run Stats Process',
'RunTrigger' => 'Run Trigger Process',
'SaveAs' => 'Save as', 'SaveAs' => 'Save as',
'SaveFilter' => 'Save Filter', 'SaveFilter' => 'Save Filter',
'SaveJPEGs' => 'Save JPEGs', 'SaveJPEGs' => 'Save JPEGs',
@ -699,6 +714,7 @@ $SLANG = array(
'Stopped' => 'Stopped', 'Stopped' => 'Stopped',
'Stop' => 'Stop', 'Stop' => 'Stop',
'StorageArea' => 'Storage Area', 'StorageArea' => 'Storage Area',
'StorageDoDelete' => 'Do Deletes',
'StorageScheme' => 'Scheme', 'StorageScheme' => 'Scheme',
'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Stream' => 'Stream', 'Stream' => 'Stream',

View File

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

View File

@ -78,6 +78,8 @@ $SLANG = array(
'Actual' => 'Actual', 'Actual' => 'Actual',
'AddNewControl' => 'Añadir nuevo control', 'AddNewControl' => 'Añadir nuevo control',
'AddNewMonitor' => 'Añadir nuevo monitor', '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', 'AddNewUser' => 'Añadir nuevo usuario',
'AddNewZone' => 'Añadir nueva zona', 'AddNewZone' => 'Añadir nueva zona',
'Alarm' => 'Alarma', 'Alarm' => 'Alarma',
@ -105,22 +107,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Estado de archivo', 'AttrArchiveStatus' => 'Estado de archivo',
'AttrAvgScore' => 'Promed. señal', 'AttrAvgScore' => 'Promed. señal',
'AttrCause' => 'Causa', 'AttrCause' => 'Causa',
'AttrDate' => 'Fecha',
'AttrDateTime' => 'Fecha/Hora',
'AttrDiskBlocks' => 'Bloques del disco', 'AttrDiskBlocks' => 'Bloques del disco',
'AttrDiskPercent' => 'Porcentaje del disco', 'AttrDiskPercent' => 'Porcentaje del disco',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Duración', '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', 'AttrFrames' => 'Marcos',
'AttrId' => 'Id', 'AttrId' => 'Id',
'AttrMaxScore' => 'Señal máxima', 'AttrMaxScore' => 'Señal máxima',
'AttrMonitorId' => 'Id monitor', 'AttrMonitorId' => 'Id monitor',
'AttrMonitorName' => 'Nombre del monitor', 'AttrMonitorName' => 'Nombre del monitor',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Nombre', 'AttrName' => 'Nombre',
'AttrNotes' => 'Notas', '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', 'AttrSystemLoad' => 'Carga del sistema',
'AttrTime' => 'Hora',
'AttrTotalScore' => 'Señal total', 'AttrTotalScore' => 'Señal total',
'AttrWeekday' => 'Día de la semana',
'Auto' => 'Auto', 'Auto' => 'Auto',
'AutoStopTimeout' => 'Autodetener tiempo de espera', 'AutoStopTimeout' => 'Autodetener tiempo de espera',
'Available' => 'Disponible', 'Available' => 'Disponible',
@ -153,9 +165,11 @@ $SLANG = array(
'BadRefBlendPerc' => 'El porcentaje de la referencia de mezcla debe ser un entero positivo', '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', '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', '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', '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', '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', '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', 'BadWidth' => 'El ancho debe tener un valor válido',
'Bandwidth' => 'Ancho de banda', '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 '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', 'ChooseLogSelection' => 'Elegir selección de registro',
'ChoosePreset' => 'Elegir preprogramación', 'ChoosePreset' => 'Elegir preprogramación',
'Clear' => 'Limpiar', 'Clear' => 'Limpiar',
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Cerrar', 'Close' => 'Cerrar',
'Colour' => 'Color', 'Colour' => 'Color',
'Command' => 'Comando', 'Command' => 'Comando',
'Component' => 'Componente', 'Component' => 'Componente',
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config', 'Config' => 'Config',
'ConfiguredFor' => 'Configurado para', 'ConfiguredFor' => 'Configurado para',
'ConfirmDeleteEvents' => '¿Seguro que desea borrar los eventos seleccionados?', 'ConfirmDeleteEvents' => '¿Seguro que desea borrar los eventos seleccionados?',
@ -280,9 +296,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Aún no, recordarme de nuevo en 1 semana', 'DonateRemindWeek' => 'Aún no, recordarme de nuevo en 1 semana',
'DonateYes' => 'Sí, me gustaría hacer una donación ahora', 'DonateYes' => 'Sí, me gustaría hacer una donación ahora',
'Download' => 'Descargar', 'Download' => 'Descargar',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicar nombre de monitor', 'DuplicateMonitorName' => 'Duplicar nombre de monitor',
'Duration' => 'Duración', 'Duration' => 'Duración',
'Edit' => 'Editar', 'Edit' => 'Editar',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email', 'Email' => 'Email',
'EnableAlarms' => 'Habilitar alarmas', 'EnableAlarms' => 'Habilitar alarmas',
'Enabled' => 'Habilitado', 'Enabled' => 'Habilitado',
@ -299,6 +317,7 @@ $SLANG = array(
'Events' => 'Eventos', 'Events' => 'Eventos',
'Exclude' => 'Excluir', 'Exclude' => 'Excluir',
'Execute' => 'Ejecutar', 'Execute' => 'Ejecutar',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exportar', 'Export' => 'Exportar',
'ExportDetails' => 'Exportar detalles de evento', 'ExportDetails' => 'Exportar detalles de evento',
'ExportFailed' => 'Fallo al exportar', 'ExportFailed' => 'Fallo al exportar',
@ -328,8 +347,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Ejecutar comando para todas las coincidencias', 'FilterExecuteEvents' => 'Ejecutar comando para todas las coincidencias',
'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterLog' => 'Filter log', // Added - 2015-04-18
'FilterMessageEvents' => 'Detalles de mensaje de todas las coincidencias', 'FilterMessageEvents' => 'Detalles de mensaje de todas las coincidencias',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtrar Px', 'FilterPx' => 'Filtrar Px',
'FilterUnset' => 'Debe especificar un ancho y un alto para el filtro', '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', 'FilterUploadEvents' => 'Subir todas las coincidencias',
'FilterVideoEvents' => 'Create video for all matches', // Added - 2011-08-23 'FilterVideoEvents' => 'Create video for all matches', // Added - 2011-08-23
'Filters' => 'Filtros', 'Filters' => 'Filtros',
@ -354,6 +375,7 @@ $SLANG = array(
'Function' => 'Función', 'Function' => 'Función',
'Gain' => 'Ganancia', 'Gain' => 'Ganancia',
'General' => 'General', 'General' => 'General',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Generate Video', // Added - 2011-08-23 'GenerateVideo' => 'Generate Video', // Added - 2011-08-23
'GeneratingVideo' => 'Generating Video', // Added - 2011-08-23 'GeneratingVideo' => 'Generating Video', // Added - 2011-08-23
'GoToZoneMinder' => 'Ir a ZoneMinder.com', 'GoToZoneMinder' => 'Ir a ZoneMinder.com',
@ -374,6 +396,7 @@ $SLANG = array(
'High' => 'Alto', 'High' => 'Alto',
'HighBW' => 'Alto&nbsp;B/B', 'HighBW' => 'Alto&nbsp;B/B',
'Home' => 'Inicio', 'Home' => 'Inicio',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Hora', 'Hour' => 'Hora',
'Hue' => 'Matiz', 'Hue' => 'Matiz',
'Id' => 'Id', 'Id' => 'Id',
@ -398,6 +421,7 @@ $SLANG = array(
'Line' => 'Línea', 'Line' => 'Línea',
'LinkedMonitors' => 'Monitores enlazados', 'LinkedMonitors' => 'Monitores enlazados',
'List' => 'Lista', 'List' => 'Lista',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Carga', 'Load' => 'Carga',
'Local' => 'Local', 'Local' => 'Local',
'Log' => 'Registro', '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/>', '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', 'Monitors' => 'Monitores',
'Montage' => 'Montaje', 'Montage' => 'Montaje',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Mes', 'Month' => 'Mes',
'More' => 'Más', 'More' => 'Más',
'MotionFrameSkip' => 'Motion Frame Skip', 'MotionFrameSkip' => 'Motion Frame Skip',
@ -510,6 +535,7 @@ $SLANG = array(
'Next' => 'Siguiente', 'Next' => 'Siguiente',
'No' => 'No', 'No' => 'No',
'NoDetectedCameras' => 'No se detectaron cámaras', 'NoDetectedCameras' => 'No se detectaron cámaras',
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoFramesRecorded' => 'No hay marcos grabados para este evento', 'NoFramesRecorded' => 'No hay marcos grabados para este evento',
'NoGroup' => 'Sin grupo', 'NoGroup' => 'Sin grupo',
'NoSavedFilters' => 'No hay filtros guardados', 'NoSavedFilters' => 'No hay filtros guardados',
@ -528,6 +554,8 @@ $SLANG = array(
'OpGt' => 'mayor que', 'OpGt' => 'mayor que',
'OpGtEq' => 'mayor que o igual a', 'OpGtEq' => 'mayor que o igual a',
'OpIn' => 'en conjunto', 'OpIn' => 'en conjunto',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'menor que', 'OpLt' => 'menor que',
'OpLtEq' => 'menor que o igual a', 'OpLtEq' => 'menor que o igual a',
'OpMatches' => 'coincidencias', 'OpMatches' => 'coincidencias',
@ -537,6 +565,7 @@ $SLANG = array(
'Open' => 'Abrir', 'Open' => 'Abrir',
'OptionHelp' => 'Ayuda de la opción', '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.', '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', 'Options' => 'Opciones',
'OrEnterNewName' => 'o introduzca un nuevo nombre', 'OrEnterNewName' => 'o introduzca un nuevo nombre',
'Order' => 'Orden', '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 '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 'Progress' => 'Progress', // Added - 2015-04-18
'Protocol' => 'Protocolo', 'Protocol' => 'Protocolo',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Valorar', 'Rate' => 'Valorar',
'Real' => 'Real', 'Real' => 'Real',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Grabar', 'Record' => 'Grabar',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge', 'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge',
'Refresh' => 'Refrescar', 'Refresh' => 'Refrescar',
'Remote' => 'Remoto', 'Remote' => 'Remoto',
@ -592,6 +625,7 @@ $SLANG = array(
'ReplayAll' => 'Todos los eventos', 'ReplayAll' => 'Todos los eventos',
'ReplayGapless' => 'Eventos sin espacios', 'ReplayGapless' => 'Eventos sin espacios',
'ReplaySingle' => 'Evento individual', 'ReplaySingle' => 'Evento individual',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'Restablecer', 'Reset' => 'Restablecer',
'ResetEventCounts' => 'Restablecer número de eventos', 'ResetEventCounts' => 'Restablecer número de eventos',
'Restart' => 'Reiniciar', 'Restart' => 'Reiniciar',
@ -610,6 +644,7 @@ $SLANG = array(
'Save' => 'Guardar', 'Save' => 'Guardar',
'SaveAs' => 'Guardar cómo', 'SaveAs' => 'Guardar cómo',
'SaveFilter' => 'Guardar filtro', 'SaveFilter' => 'Guardar filtro',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Escalar', 'Scale' => 'Escalar',
'Score' => 'Cuenta', 'Score' => 'Cuenta',
'Secs' => 'Segs', 'Secs' => 'Segs',
@ -626,6 +661,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Mostrar ventana de filtros', 'ShowFilterWindow' => 'Mostrar ventana de filtros',
'ShowTimeline' => 'Mostrar línea de tiempo', 'ShowTimeline' => 'Mostrar línea de tiempo',
'SignalCheckColour' => 'Color de comprobación de señal', 'SignalCheckColour' => 'Color de comprobación de señal',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Tamaño', 'Size' => 'Tamaño',
'SkinDescription' => 'Cambiar el tema por defecto para este ordenador', 'SkinDescription' => 'Cambiar el tema por defecto para este ordenador',
'Sleep' => 'Dormir', 'Sleep' => 'Dormir',
@ -645,6 +681,10 @@ $SLANG = array(
'State' => 'Estado', 'State' => 'Estado',
'Stats' => 'Estadísticas', 'Stats' => 'Estadísticas',
'Status' => 'Estado', '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', 'Step' => 'Salto',
'StepBack' => 'Salto atrás', 'StepBack' => 'Salto atrás',
'StepForward' => 'Salto adelante', 'StepForward' => 'Salto adelante',
@ -655,6 +695,8 @@ $SLANG = array(
'Stills' => 'Fijas', 'Stills' => 'Fijas',
'Stop' => 'Detener', 'Stop' => 'Detener',
'Stopped' => 'Detenido', 'Stopped' => 'Detenido',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Corriente', 'Stream' => 'Corriente',
'StreamReplayBuffer' => 'Secuencia de búfer de reproducción', 'StreamReplayBuffer' => 'Secuencia de búfer de reproducción',
'Submit' => 'Enviar', '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. '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', 'Timestamp' => 'Marca de tiempo',
'TimestampLabelFormat' => 'Formato de hora multinacional', 'TimestampLabelFormat' => 'Formato de hora multinacional',
'TimestampLabelSize' => 'Tamaño de fuente',
'TimestampLabelX' => 'Etiqueta de tiempo X', 'TimestampLabelX' => 'Etiqueta de tiempo X',
'TimestampLabelY' => 'Etiqueta de tiempo Y', 'TimestampLabelY' => 'Etiqueta de tiempo Y',
'TimestampLabelSize' => 'Tamaño de fuente',
'Today' => 'Hoy', 'Today' => 'Hoy',
'Tools' => 'Herramientas', 'Tools' => 'Herramientas',
'Total' => 'Total', 'Total' => 'Total',
@ -721,6 +763,7 @@ $SLANG = array(
'VideoGenParms' => 'Video Generation Parameters', // Added - 2011-08-23 'VideoGenParms' => 'Video Generation Parameters', // Added - 2011-08-23
'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2011-08-23 'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2011-08-23
'VideoSize' => 'Video Size', // Added - 2011-08-23 'VideoSize' => 'Video Size', // Added - 2011-08-23
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Ver', 'View' => 'Ver',
'ViewAll' => 'Ver todos', 'ViewAll' => 'Ver todos',
'ViewEvent' => 'Ver evento', 'ViewEvent' => 'Ver evento',
@ -730,6 +773,7 @@ $SLANG = array(
'Watch' => 'Observar', 'Watch' => 'Observar',
'Web' => 'Web', 'Web' => 'Web',
'WebColour' => 'Color web', 'WebColour' => 'Color web',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Semana', 'Week' => 'Semana',
'White' => 'Blanco', 'White' => 'Blanco',
'WhiteBalance' => 'Balance de blancos', 'WhiteBalance' => 'Balance de blancos',

View File

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

View File

@ -84,6 +84,8 @@ $SLANG = array(
'Actual' => 'Réel', 'Actual' => 'Réel',
'AddNewControl' => 'Ajouter contrôle', 'AddNewControl' => 'Ajouter contrôle',
'AddNewMonitor' => 'Ajouter caméra', 'AddNewMonitor' => 'Ajouter caméra',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewUser' => 'Ajouter utilisateur', 'AddNewUser' => 'Ajouter utilisateur',
'AddNewZone' => 'Ajouter zone', 'AddNewZone' => 'Ajouter zone',
'Alarm' => 'Alarme', 'Alarm' => 'Alarme',
@ -111,22 +113,32 @@ $SLANG = array(
'AttrArchiveStatus' => 'Etat Archive', 'AttrArchiveStatus' => 'Etat Archive',
'AttrAvgScore' => 'Score moy.', 'AttrAvgScore' => 'Score moy.',
'AttrCause' => 'Cause', 'AttrCause' => 'Cause',
'AttrDate' => 'Date',
'AttrDateTime' => 'Date/Heure',
'AttrDiskBlocks' => 'Blocs disque', 'AttrDiskBlocks' => 'Blocs disque',
'AttrDiskPercent' => '% disque', 'AttrDiskPercent' => '% disque',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDuration' => 'Durée', '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', 'AttrFrames' => 'Images',
'AttrId' => 'Id', 'AttrId' => 'Id',
'AttrMaxScore' => 'Score max.', 'AttrMaxScore' => 'Score max.',
'AttrMonitorId' => 'N°', 'AttrMonitorId' => 'N°',
'AttrMonitorName' => 'Nom caméra', 'AttrMonitorName' => 'Nom caméra',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrName' => 'Nom', 'AttrName' => 'Nom',
'AttrNotes' => 'Notes', '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', 'AttrSystemLoad' => 'Charge système',
'AttrTime' => 'Heure',
'AttrTotalScore' => 'Score total', 'AttrTotalScore' => 'Score total',
'AttrWeekday' => 'Semaine',
'Auto' => 'Auto', 'Auto' => 'Auto',
'AutoStopTimeout' => 'Temporisation arrêt', 'AutoStopTimeout' => 'Temporisation arrêt',
'Available' => 'Disponibles', // Added - 2009-03-31 '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', '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', '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', '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', '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', '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', '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', 'BadWidth' => 'La valeur de la largeur est invalide',
'Bandwidth' => 'Débit', '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 '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 'ChooseLogSelection' => 'Choisir une sélection de journaux', // Added - 2011-06-17
'ChoosePreset' => 'Choisir préréglage', 'ChoosePreset' => 'Choisir préréglage',
'Clear' => 'Effacer', // Added - 2011-06-16 'Clear' => 'Effacer', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'Close' => 'Fermer', 'Close' => 'Fermer',
'Colour' => 'Couleur', 'Colour' => 'Couleur',
'Command' => 'Commande', 'Command' => 'Commande',
'Component' => 'Composant', // Added - 2011-06-16 'Component' => 'Composant', // Added - 2011-06-16
'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30
'Config' => 'Config', 'Config' => 'Config',
'ConfiguredFor' => 'Configuré pour', 'ConfiguredFor' => 'Configuré pour',
'ConfirmDeleteEvents' => 'Etes-vous sûr de vouloir effacer le(s) événement(s) sélectionné(s)?', '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', 'DonateRemindWeek' => 'Pas encore, me rappeler dans 1 semaine',
'DonateYes' => 'Oui, je souhaiterais faire un don maintenant', 'DonateYes' => 'Oui, je souhaiterais faire un don maintenant',
'Download' => 'Télécharger', 'Download' => 'Télécharger',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Dupliquer le nom de la caméra', // Added - 2009-03-31 'DuplicateMonitorName' => 'Dupliquer le nom de la caméra', // Added - 2009-03-31
'Duration' => 'Durée', 'Duration' => 'Durée',
'Edit' => 'Editer', 'Edit' => 'Editer',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'Email' => 'Email', 'Email' => 'Email',
'EnableAlarms' => 'Activer les alarmes', 'EnableAlarms' => 'Activer les alarmes',
'Enabled' => 'Activé', 'Enabled' => 'Activé',
@ -305,6 +323,7 @@ $SLANG = array(
'Events' => 'Evénements', 'Events' => 'Evénements',
'Exclude' => 'Exclure', 'Exclude' => 'Exclure',
'Execute' => 'Exécuter', 'Execute' => 'Exécuter',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Export' => 'Exporter', 'Export' => 'Exporter',
'ExportDetails' => 'Exporter détails événements', 'ExportDetails' => 'Exporter détails événements',
'ExportFailed' => 'Exportation échouée', 'ExportFailed' => 'Exportation échouée',
@ -334,8 +353,10 @@ $SLANG = array(
'FilterExecuteEvents' => 'Exécuter une commande', 'FilterExecuteEvents' => 'Exécuter une commande',
'FilterLog' => 'Filtre', // Added - 2015-04-18 'FilterLog' => 'Filtre', // Added - 2015-04-18
'FilterMessageEvents' => 'Envoyer les détails par message', 'FilterMessageEvents' => 'Envoyer les détails par message',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Filtre Px', 'FilterPx' => 'Filtre Px',
'FilterUnset' => 'Vous devez spécifier une largeur et une hauteur de filtre', 'FilterUnset' => 'Vous devez spécifier une largeur et une hauteur de filtre',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Transférer', 'FilterUploadEvents' => 'Transférer',
'FilterVideoEvents' => 'Créer vidéo', 'FilterVideoEvents' => 'Créer vidéo',
'Filters' => 'Filtres', 'Filters' => 'Filtres',
@ -360,6 +381,7 @@ $SLANG = array(
'Function' => 'Mode', 'Function' => 'Mode',
'Gain' => 'Gain', 'Gain' => 'Gain',
'General' => 'Général', 'General' => 'Général',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateVideo' => 'Générer vidéo', 'GenerateVideo' => 'Générer vidéo',
'GeneratingVideo' => 'Génération vidéo', 'GeneratingVideo' => 'Génération vidéo',
'GoToZoneMinder' => 'Aller sur ZoneMinder.com', 'GoToZoneMinder' => 'Aller sur ZoneMinder.com',
@ -380,6 +402,7 @@ $SLANG = array(
'High' => 'Haut', 'High' => 'Haut',
'HighBW' => 'Haut débit', 'HighBW' => 'Haut débit',
'Home' => 'Maison', 'Home' => 'Maison',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hour' => 'Heure', 'Hour' => 'Heure',
'Hue' => 'Teinte', 'Hue' => 'Teinte',
'Id' => 'N°', 'Id' => 'N°',
@ -404,6 +427,7 @@ $SLANG = array(
'Line' => 'Ligne', // Added - 2011-06-16 'Line' => 'Ligne', // Added - 2011-06-16
'LinkedMonitors' => 'Caméra(s) liée(s)', 'LinkedMonitors' => 'Caméra(s) liée(s)',
'List' => 'Liste', 'List' => 'Liste',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'Load' => 'Charge', 'Load' => 'Charge',
'Local' => 'Local', 'Local' => 'Local',
'Log' => 'Journal', // Added - 2011-06-16 '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 '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', 'Monitors' => 'Caméras',
'Montage' => 'Montage', 'Montage' => 'Montage',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'Month' => 'Mois', 'Month' => 'Mois',
'More' => 'Plus', // Added - 2011-06-16 'More' => 'Plus', // Added - 2011-06-16
'MotionFrameSkip' => 'Saut image en alarme', 'MotionFrameSkip' => 'Saut image en alarme',
@ -516,6 +541,7 @@ $SLANG = array(
'Next' => 'Suivant', 'Next' => 'Suivant',
'No' => 'Non', 'No' => 'Non',
'NoDetectedCameras' => 'Pas de caméras détectées', // Added - 2009-03-31 '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', 'NoFramesRecorded' => 'Pas d\'images enregistrées pour cet événement',
'NoGroup' => 'Pas de groupe', 'NoGroup' => 'Pas de groupe',
'NoSavedFilters' => 'Pas de filtres sauvegardés', 'NoSavedFilters' => 'Pas de filtres sauvegardés',
@ -534,6 +560,8 @@ $SLANG = array(
'OpGt' => 'sup. à', 'OpGt' => 'sup. à',
'OpGtEq' => 'plus grand ou égal à', 'OpGtEq' => 'plus grand ou égal à',
'OpIn' => 'en lot', 'OpIn' => 'en lot',
'OpIs' => 'is', // Added - 2018-08-30
'OpIsNot' => 'is not', // Added - 2018-08-30
'OpLt' => 'inf. à', 'OpLt' => 'inf. à',
'OpLtEq' => 'inf. ou égal à', 'OpLtEq' => 'inf. ou égal à',
'OpMatches' => 'correspond', 'OpMatches' => 'correspond',
@ -543,6 +571,7 @@ $SLANG = array(
'Open' => 'Ouvrir', 'Open' => 'Ouvrir',
'OptionHelp' => 'Aide', 'OptionHelp' => 'Aide',
'OptionRestartWarning' => 'Ces changements peuvent nécessiter un redémarrage de ZoneMinder pour être pleinement opérationnels.', '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', 'Options' => 'Options',
'OrEnterNewName' => 'ou entrez nouv. nom', 'OrEnterNewName' => 'ou entrez nouv. nom',
'Order' => 'Ordre', '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 '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 'Progress' => 'Progression', // Added - 2015-04-18
'Protocol' => 'Protocole', 'Protocol' => 'Protocole',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'Rate' => 'Vitesse', 'Rate' => 'Vitesse',
'Real' => 'Réel', 'Real' => 'Réel',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Enregistrer', 'Record' => 'Enregistrer',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RefImageBlendPct' => '% fusion image référence', 'RefImageBlendPct' => '% fusion image référence',
'Refresh' => 'Rafraîchir', 'Refresh' => 'Rafraîchir',
'Remote' => 'Distant', 'Remote' => 'Distant',
@ -598,6 +631,7 @@ $SLANG = array(
'ReplayAll' => 'Tous les événements', 'ReplayAll' => 'Tous les événements',
'ReplayGapless' => 'Rejouer sans blancs', 'ReplayGapless' => 'Rejouer sans blancs',
'ReplaySingle' => 'Rejouer seul', 'ReplaySingle' => 'Rejouer seul',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'Reset' => 'RàZ', 'Reset' => 'RàZ',
'ResetEventCounts' => 'RàZ compteur évts', 'ResetEventCounts' => 'RàZ compteur évts',
'Restart' => 'Redémarrer', 'Restart' => 'Redémarrer',
@ -616,6 +650,7 @@ $SLANG = array(
'Save' => 'Sauvegarder', 'Save' => 'Sauvegarder',
'SaveAs' => 'Sauvegarder sous', 'SaveAs' => 'Sauvegarder sous',
'SaveFilter' => 'Sauvegarder filtre', 'SaveFilter' => 'Sauvegarder filtre',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'Scale' => 'Echelle', 'Scale' => 'Echelle',
'Score' => 'Score', 'Score' => 'Score',
'Secs' => 'Secs', 'Secs' => 'Secs',
@ -632,6 +667,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Filtres', 'ShowFilterWindow' => 'Filtres',
'ShowTimeline' => 'Afficher chronologie', 'ShowTimeline' => 'Afficher chronologie',
'SignalCheckColour' => 'Couleur vérif. signal', 'SignalCheckColour' => 'Couleur vérif. signal',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Taille', 'Size' => 'Taille',
'SkinDescription' => 'Remplacer le skin par défaut', // Added - 2011-01-30 'SkinDescription' => 'Remplacer le skin par défaut', // Added - 2011-01-30
'Sleep' => 'Veille', 'Sleep' => 'Veille',
@ -651,6 +687,10 @@ $SLANG = array(
'State' => 'Etat', 'State' => 'Etat',
'Stats' => 'Stats', 'Stats' => 'Stats',
'Status' => 'Statut', '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', 'Step' => 'Pas',
'StepBack' => 'Reculer', 'StepBack' => 'Reculer',
'StepForward' => 'Avancer', 'StepForward' => 'Avancer',
@ -661,6 +701,8 @@ $SLANG = array(
'Stills' => 'Photos', 'Stills' => 'Photos',
'Stop' => 'Arrêter', 'Stop' => 'Arrêter',
'Stopped' => 'Arrêté', 'Stopped' => 'Arrêté',
'StorageArea' => 'Storage Area', // Added - 2018-08-30
'StorageScheme' => 'Scheme', // Added - 2018-08-30
'Stream' => 'Flux', 'Stream' => 'Flux',
'StreamReplayBuffer' => 'Nb d\'image(s) pour relecture', 'StreamReplayBuffer' => 'Nb d\'image(s) pour relecture',
'Submit' => 'Soumettre', '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. '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', 'Timestamp' => 'Horodatage',
'TimestampLabelFormat' => 'Format', 'TimestampLabelFormat' => 'Format',
'TimestampLabelSize' => 'Taille de police',
'TimestampLabelX' => 'Coordonnée X', 'TimestampLabelX' => 'Coordonnée X',
'TimestampLabelY' => 'Coordonnée Y', 'TimestampLabelY' => 'Coordonnée Y',
'TimestampLabelSize' => 'Taille de police',
'Today' => 'Aujourd\'hui', 'Today' => 'Aujourd\'hui',
'Tools' => 'Outils', 'Tools' => 'Outils',
'Total' => 'Total', // Added - 2011-06-16 'Total' => 'Total', // Added - 2011-06-16
@ -727,6 +769,7 @@ $SLANG = array(
'VideoGenParms' => 'Paramètres génération vidéo', 'VideoGenParms' => 'Paramètres génération vidéo',
'VideoGenSucceeded' => 'Vidéo générée avec succès !', 'VideoGenSucceeded' => 'Vidéo générée avec succès !',
'VideoSize' => 'Taille vidéo', 'VideoSize' => 'Taille vidéo',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Voir', 'View' => 'Voir',
'ViewAll' => 'Tout voir', 'ViewAll' => 'Tout voir',
'ViewEvent' => 'Voir événement', 'ViewEvent' => 'Voir événement',
@ -736,6 +779,7 @@ $SLANG = array(
'Watch' => 'Regarder', 'Watch' => 'Regarder',
'Web' => 'Web', 'Web' => 'Web',
'WebColour' => 'Couleur web', 'WebColour' => 'Couleur web',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'Week' => 'Semaine', 'Week' => 'Semaine',
'White' => 'Blanc', 'White' => 'Blanc',
'WhiteBalance' => 'Balance des blancs', 'WhiteBalance' => 'Balance des blancs',

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