├── www ├── servo_off ├── pilight_off ├── pipan_off ├── diskUsage.txt ├── status_mjpeg.txt ├── css │ ├── extrastyle.txt │ ├── es_Default.css │ ├── preview.css │ ├── es_Night.css │ └── es_Modern.css ├── convertCmd.txt ├── userLevelExample ├── macros │ ├── error_hard.sh │ ├── test.sh │ └── startstopX.sh ├── image.png ├── video.png ├── delete.png ├── loading.jpg ├── timelapse.png ├── updating.jpg ├── unavailable.jpg ├── multiview.jsonD ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── cam_get.php ├── cmd_pipe.php ├── status_mjpeg.php ├── userbuttonsD ├── cam_pic.php ├── min.php ├── uPresetsv2.html ├── cam_picLatestTL.php ├── cmd_func.php ├── uPresetsP-OV5647.html ├── uPresetsN-OV5647.html ├── pirTrigger.py ├── uPresetsP-IMX219.html ├── Copyright.txt ├── uPresetsN-IMX219.html ├── cam_pic_new.php ├── multiview.html ├── multiview.php ├── pipan.php ├── js │ ├── preview.js │ ├── pipan.js │ └── script.js ├── motion.php ├── config.php ├── preview.php └── index.php ├── bin ├── raspimjpeg ├── raspimjpeg-buster └── raspimjpeg-stretch ├── etc ├── apache2 │ ├── conf.d │ │ └── other-vhosts-access-log │ └── sites-available │ │ ├── raspicam.conf.1 │ │ └── default.1 ├── sudoers.d │ └── RPI_Cam_Web_Interface ├── php5 │ └── apc.ini ├── rc_local_run │ └── rc.local.1 ├── nginx │ └── sites-available │ │ └── rpicam.1 ├── raspimjpeg │ └── raspimjpeg.1 └── motion │ └── motion.conf.1 ├── .gitignore ├── SECURITY.md ├── README.md ├── LICENSE.txt ├── installreadme.txt ├── stop.sh ├── debug.sh ├── start.sh ├── update.sh ├── cam.sh ├── remove.sh └── install.sh /www/servo_off: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /www/pilight_off: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /www/pipan_off: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /www/diskUsage.txt: -------------------------------------------------------------------------------- 1 | local:local -------------------------------------------------------------------------------- /www/status_mjpeg.txt: -------------------------------------------------------------------------------- 1 | ready -------------------------------------------------------------------------------- /www/css/extrastyle.txt: -------------------------------------------------------------------------------- 1 | es_Default.css -------------------------------------------------------------------------------- /www/convertCmd.txt: -------------------------------------------------------------------------------- 1 | /usr/bin/ffmpeg -f image2 -i i_%05d.jpg -------------------------------------------------------------------------------- /www/userLevelExample: -------------------------------------------------------------------------------- 1 | jim:0 2 | fred:3 3 | charlie:6 4 | -------------------------------------------------------------------------------- /www/css/es_Default.css: -------------------------------------------------------------------------------- 1 | /*! default extra styles no changes */ 2 | -------------------------------------------------------------------------------- /www/macros/error_hard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo shutdown -r now 3 | -------------------------------------------------------------------------------- /www/macros/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | date >/var/www/testmacro.txt 3 | -------------------------------------------------------------------------------- /www/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/image.png -------------------------------------------------------------------------------- /www/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/video.png -------------------------------------------------------------------------------- /bin/raspimjpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/bin/raspimjpeg -------------------------------------------------------------------------------- /www/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/delete.png -------------------------------------------------------------------------------- /www/loading.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/loading.jpg -------------------------------------------------------------------------------- /www/timelapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/timelapse.png -------------------------------------------------------------------------------- /www/updating.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/updating.jpg -------------------------------------------------------------------------------- /www/unavailable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/unavailable.jpg -------------------------------------------------------------------------------- /bin/raspimjpeg-buster: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/bin/raspimjpeg-buster -------------------------------------------------------------------------------- /bin/raspimjpeg-stretch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/bin/raspimjpeg-stretch -------------------------------------------------------------------------------- /www/multiview.jsonD: -------------------------------------------------------------------------------- 1 | {"hosts":["http://192.168.0.2/","http://192.168.0.3/","http://192.168.0.4/","http://192.168.0.5/"],"delays":[40000,40000,40000,40000]} -------------------------------------------------------------------------------- /www/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /www/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /www/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /www/cam_get.php: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /www/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silvanmelchior/RPi_Cam_Web_Interface/HEAD/www/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /etc/apache2/conf.d/other-vhosts-access-log: -------------------------------------------------------------------------------- 1 | # Define an access log for VirtualHosts that don't define their own logfile 2 | # CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined 3 | -------------------------------------------------------------------------------- /etc/sudoers.d/RPI_Cam_Web_Interface: -------------------------------------------------------------------------------- 1 | # allow 'shutdown' command via web server 2 | www-data ALL=/sbin/shutdown 3 | www-data ALL=NOPASSWD: /sbin/shutdown 4 | www-data ALL=/bin/date 5 | www-data ALL=NOPASSWD: /bin/date 6 | -------------------------------------------------------------------------------- /etc/php5/apc.ini: -------------------------------------------------------------------------------- 1 | extension=apc.so 2 | apc.enabled=1 3 | apc.file_update_protection=2 4 | apc.shm_size=32M 5 | apc.include_once_override=0 6 | apc.shm_segments=1 7 | apc.gc_ttl=7200 8 | apc.num_files_hint=1024 9 | apc.enable_cli=0 10 | -------------------------------------------------------------------------------- /www/cmd_pipe.php: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp # Vim Files 2 | *~ # backup files 3 | 4 | # Generated text files 5 | install.txt 6 | config.txt 7 | install.txt 8 | start.txt 9 | update.txt 10 | 11 | # website config 12 | etc/nginx/sites-available/default 13 | 14 | # settings 15 | etc/raspimjpeg/raspimjpeg 16 | -------------------------------------------------------------------------------- /www/status_mjpeg.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /www/userbuttonsD: -------------------------------------------------------------------------------- 1 | #User buttons definitions 2 | #maximum of twelve buttons 3 | #button name,macro,button styles,other attributes 4 | Button One,button1.sh,btn btn-danger btn-lg,style="width:50%" autofocus 5 | #Warning Button,button2.sh,btn btn-warning btn-lg,style="width:50%" 6 | #Success Button,button3.sh,btn btn-success btn-lg,style="width:50%" 7 | -------------------------------------------------------------------------------- /www/cam_pic.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Current version as reported in README.md 6 | | 7 | 8 | ## Reporting a Vulnerability 9 | 10 | email robert@tideys.co.uk with details of vulnerability found and any suggested remedies. 11 | 12 | You will receive an email response (typically within 48 hours describing action that will be taken 13 | -------------------------------------------------------------------------------- /www/min.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RPi Cam Preview 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 | 13 | -------------------------------------------------------------------------------- /www/uPresetsv2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /www/cam_picLatestTL.php: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /www/css/preview.css: -------------------------------------------------------------------------------- 1 | /*! preview.css classes for elements on preview form */ 2 | 3 | fieldset.fileicon { 4 | display:inline; 5 | margin:5px; 6 | border:2px solid LightGray; 7 | } 8 | 9 | legend.fileicon { 10 | float:left; 11 | margin-top:5px; 12 | margin-bottom:0; 13 | padding-top:0; 14 | padding-bottom:0; 15 | } 16 | 17 | button.fileicondelete { 18 | border:none; 19 | width:24px; 20 | height:24px; 21 | background-color: Transparent; 22 | background-repeat:no-repeat; 23 | background-size:24px 24px; 24 | } 25 | 26 | checkbox.fileicon { 27 | float:right; 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Web based interface for controlling the Raspberry Pi Camera, includes motion detection, time lapse, and image and video recording. 2 | Current version 6.6.26 3 | All information on this project can be found here: http://www.raspberrypi.org/forums/viewtopic.php?f=43&t=63276 4 | 5 | The wiki page can be found here: 6 | 7 | http://elinux.org/RPi-Cam-Web-Interface 8 | 9 | This includes the installation instructions at the top and full technical details. 10 | For latest change details see: 11 | 12 | https://github.com/silvanmelchior/RPi_Cam_Web_Interface/commits/master 13 | 14 | This version has updates for php7.3 / Buster. May need further changes for nginx -------------------------------------------------------------------------------- /www/css/es_Night.css: -------------------------------------------------------------------------------- 1 | /*! night.css classes dark viewing conditions */ 2 | 3 | body { 4 | background-color: #202020; 5 | } 6 | 7 | .btn-default { 8 | background-color: #404040; 9 | color: #C0C0C0; 10 | } 11 | 12 | .panel-default>.panel-heading { 13 | background-color: #404040; 14 | color: #C0C0C0; 15 | } 16 | 17 | .panel-collapse { 18 | background-color: #202020; 19 | color: #C0C0C0; 20 | } 21 | .settingsTable input{ 22 | background-color: #202020; 23 | color: #C0C0C0; 24 | } 25 | .settingsTable select{ 26 | background-color: #202020; 27 | color: #C0C0C0; 28 | } 29 | 30 | fieldset { 31 | color:#808080; 32 | } 33 | 34 | #background { 35 | background-color: #202020; 36 | } 37 | -------------------------------------------------------------------------------- /etc/apache2/sites-available/raspicam.conf.1: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | DocumentRoot /var/www 4 | 5 | Options Indexes FollowSymLinks MultiViews 6 | AllowOverride None 7 | Order allow,deny 8 | Allow from all 9 | 10 | ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ 11 | 12 | AllowOverride None 13 | Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch 14 | Order allow,deny 15 | Allow from all 16 | 17 | 18 | ErrorLog ${APACHE_LOG_DIR}/error.log 19 | LogLevel warn 20 | 21 | SetEnvIf Request_URI "/cam_pic.php$|/status_mjpeg.php$" dontlog 22 | CustomLog ${APACHE_LOG_DIR}/access.log common env=!dontlog 23 | 24 | 25 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 26 | -------------------------------------------------------------------------------- /www/cmd_func.php: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /etc/rc_local_run/rc.local.1: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # 3 | # rc.local for raspimjpeg 4 | # 5 | # This script is executed at the end of each multiuser runlevel. 6 | # Make sure that the script will "exit 0" on success or any other 7 | # value on error. 8 | # 9 | _IP=$(hostname -I) || true 10 | if [ "$_IP" ]; then 11 | printf "My IP address is %s\n" "$_IP" 12 | fi 13 | 14 | #START RASPIMJPEG SECTION 15 | mkdir -p /dev/shm/mjpeg 16 | chown www-data:www-data /dev/shm/mjpeg 17 | chmod 777 /dev/shm/mjpeg 18 | sleep 4;su -c 'raspimjpeg > /dev/null 2>&1 &' www-data 19 | if [ -e /etc/debian_version ]; then 20 | sleep 4;su -c "php /var/www/schedule.php > /dev/null 2>&1 &" www-data 21 | else 22 | sleep 4;su -s '/bin/bash' -c "php /var/www/schedule.php > /dev/null 2>&1 &" www-data 23 | fi 24 | #END RASPIMJPEG SECTION 25 | 26 | exit 0 27 | -------------------------------------------------------------------------------- /etc/nginx/sites-available/rpicam.1: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | 5 | root /var/www; 6 | index index.php index.html index.htm; 7 | server_name localhost; 8 | 9 | location / { 10 | try_files $uri $uri/ /index.html; 11 | } 12 | location ~ [^/]\.php(/|$) { 13 | fastcgi_split_path_info ^(.+?\.php)(/.*)$; 14 | if (!-f $document_root$fastcgi_script_name) { 15 | return 404; 16 | } 17 | fastcgi_pass unix:/var/run/php5-fpm.sock; 18 | fastcgi_index index.php; 19 | include fastcgi_params; 20 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 21 | fastcgi_param REMOTE_USER $remote_user; 22 | auth_basic "Off"; 23 | #auth_basic_user_file /usr/local/.htpasswd; 24 | } 25 | location ~ /\.ht { 26 | deny all; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /etc/apache2/sites-available/default.1: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | 4 | DocumentRoot /var/www 5 | 6 | Options FollowSymLinks 7 | AllowOverride None 8 | 9 | 10 | Options Indexes FollowSymLinks MultiViews 11 | AllowOverride None 12 | Order allow,deny 13 | allow from all 14 | 15 | 16 | ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ 17 | 18 | AllowOverride None 19 | Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch 20 | Order allow,deny 21 | Allow from all 22 | 23 | 24 | ErrorLog ${APACHE_LOG_DIR}/error.log 25 | 26 | # Possible values include: debug, info, notice, warn, error, crit, 27 | # alert, emerg. 28 | LogLevel warn 29 | 30 | SetEnvIf Request_URI "/cam_pic.php$|/status_mjpeg.php$" dontlog 31 | CustomLog ${APACHE_LOG_DIR}/access.log common env=!dontlog 32 | 33 | -------------------------------------------------------------------------------- /www/uPresetsP-OV5647.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #Version 1.x (OV5647) 10 | 11 | # Mode Size Aspect Ratio Frame rates FOV Binning 12 | # 0 automatic selection 13 | # 1 1920x1080 16:9 1-30fps Partial None 14 | # 2 2592x1944 4:3 1-15fps Full None 15 | # 3 2592x1944 4:3 0.1666-1fps Full None 16 | # 4 1296x972 4:3 1-42fps Full 2x2 17 | # 5 1296x730 16:9 1-49fps Full 2x2 18 | # 6 640x480 4:3 42.1-60fps Full 2x2 plus skip 19 | # 7 640x480 4:3 60.1-90fps Full 2x2 plus skip 20 | # ref - https://www.raspberrypi.org/documentation/raspbian/applications/camera.md 21 | -------------------------------------------------------------------------------- /www/uPresetsN-OV5647.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | #Version 1.x (OV5647) 11 | 12 | # Mode Size Aspect Ratio Frame rates FOV Binning 13 | # 0 automatic selection 14 | # 1 1920x1080 16:9 1-30fps Partial None 15 | # 2 2592x1944 4:3 1-15fps Full None 16 | # 3 2592x1944 4:3 0.1666-1fps Full None 17 | # 4 1296x972 4:3 1-42fps Full 2x2 18 | # 5 1296x730 16:9 1-49fps Full 2x2 19 | # 6 640x480 4:3 42.1-60fps Full 2x2 plus skip 20 | # 7 640x480 4:3 60.1-90fps Full 2x2 plus skip 21 | # ref - https://www.raspberrypi.org/documentation/raspbian/applications/camera.md 22 | -------------------------------------------------------------------------------- /www/pirTrigger.py: -------------------------------------------------------------------------------- 1 | # This python program monitors a PIR sensor attached to a GPIO 2 | # and then sends a motion start event to the scheduler 3 | # The scheduler should be set up to perform a command in the Motion Start field 4 | # Leave Motion Stop field blank 5 | # Leave Motion detection disabled to use just the PIR trigger 6 | # An example command would be ca 1 10 to do a 10 second video when PIR fires 7 | # Script could be set up to start at boot (e.g. in /etc.rc.local 8 | # Make sure to include an & 0n the end to ensure it starts in the background. 9 | # An alternative is to write the command explicitly in fifo.write and use FIFO instead of FIFO1 10 | 11 | import RPi.GPIO as GPIO 12 | import time 13 | 14 | GPIO.setmode(GPIO.BCM) 15 | GPIO.setwarnings(False) 16 | PIR_PIN = 14 17 | GPIO.setup(PIR_PIN, GPIO.IN) 18 | 19 | time.sleep(1) 20 | 21 | try: 22 | while True: 23 | if GPIO.input(PIR_PIN): 24 | fifo = open("/var/www/html/FIFO1", "w") 25 | fifo.write("1") 26 | fifo.close() 27 | while GPIO.input(PIR_PIN): 28 | time.sleep(1) 29 | time.sleep(1) 30 | except KeyboardInterrupt: 31 | pass 32 | finally: 33 | GPIO.cleanup() -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Silvan Melchior, Robert Tidey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /www/uPresetsP-IMX219.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | # Version 2.x (IMX219) 12 | 13 | # Mode Size Aspect Ratio Frame rates FOV Binning 14 | # 0 automatic selection 15 | # 1 1920x1080 16:9 0.1-30fps Partial None 16 | # 2 3280x2464 4:3 0.1-15fps Full None 17 | # 3 3280x2464 4:3 0.1-15fps Full None 18 | # 4 1640x1232 4:3 0.1-40fps Full 2x2 19 | # 5 1640x922 16:9 0.1-40fps Full 2x2 20 | # 6 1280x720 16:9 40-90fps Partial 2x2 21 | # 7 640x480 4:3 40-90fps Partial 2x2 22 | # Ref. https://www.raspberrypi.org/documentation/raspbian/applications/camera.md 23 | -------------------------------------------------------------------------------- /www/Copyright.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Silvan Melchior 2 | All rights reserved. 3 | 4 | Redistribution and use in source , with or without modification, are permitted 5 | provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Neither the name of the copyright holder nor the 9 | names of its contributors may be used to endorse or promote products 10 | derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 16 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /www/macros/startstopX.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # example start up script which converts any existing .h264 files into MP4 3 | 4 | MACRODIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 5 | BASEDIR="$( cd "$( dirname "${MACRODIR}" )" >/dev/null 2>&1 && pwd )" 6 | mypidfile=${MACRODIR}/startstop.sh.pid 7 | mylogfile=${BASEDIR}/scheduleLog.txt 8 | 9 | #Check if script already running 10 | NOW=`date +"-%Y/%m/%d %H:%M:%S-"` 11 | if [ -f $mypidfile ]; then 12 | echo "${NOW} Script already running..." >> ${mylogfile} 13 | exit 14 | fi 15 | #Remove PID file when exiting 16 | trap "rm -f -- '$mypidfile'" EXIT 17 | 18 | echo $$ > "$mypidfile" 19 | 20 | #Do conversion 21 | if [ "$1" == "start" ]; then 22 | cd ${MACRODIR} 23 | cd ../media 24 | shopt -s nullglob 25 | for f in *.h264 26 | do 27 | f1=${f%.*}.mp4 28 | NOW=`date +"-%Y/%m/%d %H:%M:%S-"` 29 | echo "${NOW} Converting $f" >> ${mylogfile} 30 | #set -e;MP4Box -fps 25 -add $f $f1 > /dev/null 2>&1;rm $f; 31 | if MP4Box -fps 25 -add $f $f1; then 32 | NOW=`date +"-%Y/%m/%d %H:%M:%S-"` 33 | echo "${NOW} Conversion complete, removing $f" >> ${mylogfile} 34 | rm $f 35 | else 36 | NOW=`date +"-%Y/%m/%d %H:%M:%S-"` 37 | echo "${NOW} Error with $f" >> ${mylogfile} 38 | fi 39 | done 40 | fi 41 | -------------------------------------------------------------------------------- /www/uPresetsN-IMX219.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | # Version 2.x (IMX219) 12 | # H264 Encoder Limits are 1920 X 1080 based on the GPU - Exploring Higher Frame Rate options and how that might be recordable - They just don't record video and the button does not work. 13 | 14 | # Mode Size Aspect Ratio Frame rates FOV Binning 15 | # 0 automatic selection 16 | # 1 1920x1080 16:9 0.1-30fps Partial None 17 | # 2 3280x2464 4:3 0.1-15fps Full None 18 | # 3 3280x2464 4:3 0.1-15fps Full None 19 | # 4 1640x1232 4:3 0.1-40fps Full 2x2 20 | # 5 1640x922 16:9 0.1-40fps Full 2x2 21 | # 6 1280x720 16:9 40-90fps Partial 2x2 22 | # 7 640x480 4:3 40-90fps Partial 2x2 23 | # Ref. https://www.raspberrypi.org/documentation/raspbian/applications/camera.md 24 | -------------------------------------------------------------------------------- /www/cam_pic_new.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RPi Cam Multiview 5 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /www/multiview.php: -------------------------------------------------------------------------------- 1 | stop.txt 35 | BASH_XTRACEFD="5" 36 | set -x 37 | 38 | cd $(dirname $(readlink -f $0)) 39 | 40 | source ./config.txt 41 | 42 | fn_stop () 43 | { # This is function stop 44 | sudo killall raspimjpeg 2>/dev/null 45 | sudo killall php 2>/dev/null 46 | sudo killall motion 2>/dev/null 47 | } 48 | 49 | #stop operation 50 | fn_stop 51 | -------------------------------------------------------------------------------- /www/css/es_Modern.css: -------------------------------------------------------------------------------- 1 | /*! Modern.css classes dark viewing conditions */ 2 | 3 | body { 4 | background-color: #181F2C; 5 | color: #ffffff; 6 | } 7 | 8 | 9 | 10 | .btn-default { 11 | background-color: #404040; 12 | color: #C0C0C0; 13 | } 14 | 15 | .panel-default{ 16 | background-color: #4D5A71; 17 | color: #ffffff; 18 | border-width: 0px; 19 | border-radius: 30px; 20 | margin-left: 10%; 21 | margin-right: 10%; 22 | } 23 | 24 | .panel-default>.panel-heading { 25 | background-color: #4D5A71; 26 | color: #ffffff; 27 | border-width: 0px; 28 | border-radius: 15px; 29 | } 30 | 31 | .panel-collapse { 32 | background-color: #1C2433; 33 | color: #ffffff; 34 | } 35 | .settingsTable input{ 36 | background-color: #212A3B; 37 | color: #586B90; 38 | margin: 5px; 39 | padding: 2px; 40 | min-width: 60px; 41 | border-color: #586B90; 42 | border-width: 2px; 43 | border-radius: 15px; 44 | text-align: center; 45 | -moz-appearance: textfield; 46 | } 47 | .settingsTable select{ 48 | background-color: #212A3B; 49 | color: #586B90; 50 | margin: 5px; 51 | border-color: #586B90; 52 | border-radius: 15px; 53 | border-width: 2px; 54 | text-align: center; 55 | } 56 | 57 | fieldset { 58 | color:#586B90; 59 | background-color: #212A3B; 60 | border-color: #586B90; 61 | border-radius: 15px; 62 | padding: 10px; 63 | } 64 | 65 | fieldset.fileicon { 66 | border-color: #586B90; 67 | border-width: 2px; 68 | } 69 | 70 | 71 | input{ 72 | background-color: #212A3B; 73 | color: #586B90; 74 | margin: 5px; 75 | padding: 2px; 76 | min-width: 60px; 77 | border-color: #586B90; 78 | border-width: 2px; 79 | border-radius: 15px; 80 | text-align: center; 81 | -moz-appearance: textfield; 82 | } 83 | 84 | select{ 85 | background-color: #212A3B; 86 | color: #586B90; 87 | margin: 5px; 88 | border-color: #586B90; 89 | border-radius: 15px; 90 | border-width: 2px; 91 | text-align: center; 92 | } 93 | 94 | #background { 95 | background-color: #131313; 96 | } 97 | -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2015, Bob Tidey 4 | # All rights reserved. 5 | 6 | # Redistribution and use, with or without modification, are permitted provided 7 | # that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Neither the name of the copyright holder nor the 11 | # names of its contributors may be used to endorse or promote products 12 | # derived from this software without specific prior written permission. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 18 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | # Description 26 | # This script stops a running RPi_Cam interface 27 | # Based on RPI_Cam_Web_Interface installer by Silvan Melchior 28 | # Edited by jfarcher to work with github 29 | # Edited by slabua to support custom installation folder 30 | # Additions by btidey, miraaz, gigpi 31 | # Split up and refactored by Bob Tidey 32 | 33 | #Debug enable next 3 lines 34 | exec 5> start.txt 35 | BASH_XTRACEFD="5" 36 | set -x 37 | 38 | cd $(dirname $(readlink -f $0)) 39 | 40 | source ./config.txt 41 | 42 | fn_stop () 43 | { # This is function stop 44 | sudo killall raspimjpeg 2>/dev/null 45 | sudo killall php 2>/dev/null 46 | sudo killall motion 2>/dev/null 47 | } 48 | 49 | #start operation 50 | fn_stop 51 | sudo mkdir -p /dev/shm/mjpeg 52 | sudo chown www-data:www-data /dev/shm/mjpeg 53 | sudo chmod 777 /dev/shm/mjpeg 54 | sleep 1;sudo su -c 'raspimjpeg &' www-data 55 | if [ -e /etc/debian_version ]; then 56 | sleep 1;sudo su -c "php /var/www/$rpicamdir/schedule.php > /dev/null &" www-data 57 | else 58 | sleep 1;sudo su -c '/bin/bash' -c "php /var/www/$rpicamdir/schedule.php > /dev/null &" www-data 59 | fi 60 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2015, Bob Tidey 4 | # All rights reserved. 5 | 6 | # Redistribution and use, with or without modification, are permitted provided 7 | # that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Neither the name of the copyright holder nor the 11 | # names of its contributors may be used to endorse or promote products 12 | # derived from this software without specific prior written permission. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 18 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | # Description 26 | # This script stops a running RPi_Cam interface 27 | # Based on RPI_Cam_Web_Interface installer by Silvan Melchior 28 | # Edited by jfarcher to work with github 29 | # Edited by slabua to support custom installation folder 30 | # Additions by btidey, miraaz, gigpi 31 | # Split up and refactored by Bob Tidey 32 | 33 | #Debug enable next 3 lines 34 | exec 5> start.txt 35 | BASH_XTRACEFD="5" 36 | set -x 37 | 38 | cd $(dirname $(readlink -f $0)) 39 | 40 | source ./config.txt 41 | 42 | fn_stop () 43 | { # This is function stop 44 | sudo killall raspimjpeg 2>/dev/null 45 | sudo killall php 2>/dev/null 46 | sudo killall motion 2>/dev/null 47 | } 48 | 49 | #start operation 50 | fn_stop 51 | sudo mkdir -p /dev/shm/mjpeg 52 | sudo chown www-data:www-data /dev/shm/mjpeg 53 | sudo chmod 777 /dev/shm/mjpeg 54 | sleep 1;sudo su -c 'raspimjpeg > /dev/null &' www-data 55 | if [ -e /etc/debian_version ]; then 56 | sleep 1;sudo su -c "php /var/www/$rpicamdir/schedule.php > /dev/null &" www-data 57 | else 58 | sleep 1;sudo su -c '/bin/bash' -c "php /var/www/$rpicamdir/schedule.php > /dev/null &" www-data 59 | fi 60 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2015, Bob Tidey 4 | # All rights reserved. 5 | 6 | # Redistribution and use, with or without modification, are permitted provided 7 | # that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Neither the name of the copyright holder nor the 11 | # names of its contributors may be used to endorse or promote products 12 | # derived from this software without specific prior written permission. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 18 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | # Description 26 | # This script updates the installer for the browser-interface to control the RPi Cam. It can be run 27 | # on any Raspberry Pi with a newly installed raspbian and enabled camera-support. 28 | # Based on RPI_Cam_WEb_Interface installer by Silvan Melchior 29 | # Edited by jfarcher to work with github 30 | # Edited by slabua to support custom installation folder 31 | # Additions by btidey, miraaz, gigpi 32 | # Split up and refactored by Bob Tidey 33 | 34 | #Debug enable next 3 lines 35 | exec 5> update.txt 36 | BASH_XTRACEFD="5" 37 | set -x 38 | 39 | cd $(dirname $(readlink -f $0)) 40 | 41 | # Terminal colors 42 | color_red="tput setaf 1" 43 | color_green="tput setaf 2" 44 | color_reset="tput sgr0" 45 | 46 | 47 | fn_abort() 48 | { 49 | $color_red; echo >&2 ' 50 | *************** 51 | *** ABORTED *** 52 | *************** 53 | ' 54 | echo "An error occurred. Exiting..." >&2; $color_reset 55 | exit 1 56 | } 57 | 58 | trap 'fn_abort' 0 59 | set -e 60 | remote=$( 61 | git ls-remote -h origin master | 62 | awk '{print $1}' 63 | ) 64 | local=$(git rev-parse HEAD) 65 | printf "Local : %s\nRemote: %s\n" $local $remote 66 | if [[ $local == $remote ]]; then 67 | dialog --title 'Update message' --infobox 'Commits match. Nothing update.' 4 35 ; sleep 2 68 | else 69 | dialog --title 'Update message' --infobox "Commits don't match. We update." 4 35 ; sleep 2 70 | git fetch origin master 71 | git reset --hard origin/master 72 | chmod u+x *.sh 73 | fi 74 | trap : 0 75 | dialog --title 'Update message' --infobox 'Update finished.' 4 20 ; sleep 2 76 | 77 | # We call updated install script passing through any quiet parameter 78 | if [ $# -eq 0 ]; then 79 | ./install.sh 80 | else 81 | ./install.sh $1 82 | fi 83 | -------------------------------------------------------------------------------- /cam.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd $(dirname $(readlink -f $0)) 4 | 5 | if [ $(dpkg-query -W -f='${Status}' "dialog" 2>/dev/null | grep -c "ok installed") -eq 0 ]; then 6 | sudo apt-get install -y dialog 7 | fi 8 | 9 | # Terminal colors 10 | color_red="tput setaf 1" 11 | color_green="tput setaf 2" 12 | color_reset="tput sgr0" 13 | RED=$(tput setaf 1) 14 | GREEN=$(tput setaf 2) 15 | NORMAL=$(tput sgr0) 16 | 17 | ASK_TO_REBOOT=0 18 | RASPICONFIG=/boot/config.txt 19 | MAINSCRIPT="./RPi_Cam_Web_Interface_Installer.sh" 20 | 21 | # Version stuff moved out functions as we need it more when one time. 22 | versionfile="./www/config.php" 23 | version=$(cat $versionfile | grep "'APP_VERSION'" | cut -d "'" -f4) 24 | backtitle="Copyright (c) 2014, Silvan Melchior. RPi Cam $version" 25 | 26 | get_config_var() { 27 | lua - "$1" "$2" < "$3.bak" 48 | local key=assert(arg[1]) 49 | local value=assert(arg[2]) 50 | local fn=assert(arg[3]) 51 | local file=assert(io.open(fn)) 52 | local made_change=false 53 | for line in file:lines() do 54 | if line:match("^#?%s*"..key.."=.*$") then 55 | line=key.."="..value 56 | made_change=true 57 | end 58 | print(line) 59 | end 60 | 61 | if not made_change then 62 | print(key.."="..value) 63 | end 64 | EOF 65 | mv "$3.bak" "$3" 66 | } 67 | 68 | do_finish() { 69 | if [ $ASK_TO_REBOOT -eq 1 ]; then 70 | dialog --yesno "Would you like to reboot now?" 5 35 71 | if [ $? -eq 0 ]; then # yes 72 | sync 73 | reboot 74 | elif [ $? -eq 1 ]; then # no 75 | exec sudo $MAINSCRIPT 76 | fi 77 | fi 78 | exit 0 79 | } 80 | 81 | do_camera () 82 | { 83 | if [ ! -e /boot/start_x.elf ]; then 84 | dialog --msgbox "Your firmware appears to be out of date (no start_x.elf). Please update" 20 60 85 | exec sudo $MAINSCRIPT 86 | fi 87 | dialog --title "Raspberry Pi camera message" \ 88 | --backtitle "$backtitle" \ 89 | --extra-button --extra-label Disable \ 90 | --ok-label Enable \ 91 | --yesno "Enable support for Raspberry Pi camera?" 5 48 92 | response=$? 93 | case $response in 94 | 0) #echo "[Enable] key pressed." 95 | set_config_var start_x 1 $RASPICONFIG 96 | CUR_GPU_MEM=$(get_config_var gpu_mem $RASPICONFIG) 97 | if [ -z "$CUR_GPU_MEM" ] || [ "$CUR_GPU_MEM" -lt 128 ]; then 98 | set_config_var gpu_mem 128 $RASPICONFIG 99 | fi 100 | sed $RASPICONFIG -i -e "s/^startx/#startx/" 101 | sed $RASPICONFIG -i -e "s/^fixup_file/#fixup_file/" 102 | ASK_TO_REBOOT=1 103 | do_finish 104 | ;; 105 | 1) #echo "[Cansel] key pressed." 106 | exec sudo $MAINSCRIPT 107 | ;; 108 | 3) #echo "[Disable] key pressed." 109 | set_config_var start_x 0 $RASPICONFIG 110 | sed $RASPICONFIG -i -e "s/^startx/#startx/" 111 | sed $RASPICONFIG -i -e "s/^start_file/#start_file/" 112 | sed $RASPICONFIG -i -e "s/^fixup_file/#fixup_file/" 113 | ASK_TO_REBOOT=1 114 | do_finish 115 | ;; 116 | 255) echo "[ESC] key pressed." 117 | exec sudo $MAINSCRIPT 118 | ;; 119 | esac 120 | } 121 | 122 | do_camera 123 | exit 124 | -------------------------------------------------------------------------------- /www/pipan.php: -------------------------------------------------------------------------------- 1 | 165, 17 | 'y' => 165, 18 | 'left' => 'Xplus', 19 | 'right' => 'Xminus', 20 | 'up' => 'Yminus', 21 | 'down' => 'Yplus', 22 | 'XMax' => 235, 23 | 'XMin' => 95, 24 | 'XStep' => 7, 25 | 'YMax' => 235, 26 | 'YMin' => 95, 27 | 'YStep' => 7 28 | ); 29 | 30 | // 31 | // code for pipan 32 | // 33 | if(isset($_GET["pan"])) { 34 | if(is_numeric($_GET["pan"])) { 35 | if(is_numeric($_GET["tilt"])) { 36 | $pan = round($min_pan + (($max_pan - $min_pan)/200*$_GET["pan"])); 37 | $tilt = round($min_tilt + (($max_tilt - $min_tilt)/200*$_GET["tilt"])); 38 | $pipe = fopen("FIFO_pipan","w"); 39 | fwrite($pipe, "servo $pan $tilt "); 40 | fclose($pipe); 41 | file_put_contents("pipan_bak.txt", $_GET["pan"] . " " . $_GET["tilt"]); 42 | } 43 | } 44 | } 45 | 46 | if(isset($_GET["red"])) { 47 | if(is_numeric($_GET["red"])) { 48 | if(is_numeric($_GET["green"])) { 49 | if(is_numeric($_GET["blue"])) { 50 | $pipe = fopen("FIFO_pipan","w"); 51 | fwrite($pipe, "led " . $_GET["red"] . " " . $_GET["green"] . " " . $_GET["blue"] . " "); 52 | fclose($pipe); 53 | } 54 | } 55 | } 56 | } 57 | // 58 | // code for servo 59 | // 60 | if(isset($_GET["action"])) { 61 | try { 62 | $input = json_decode(file_get_contents(BASE_DIR . '/' . SERVO_DATA), true); 63 | foreach($servoData as $key => $value) { 64 | if (array_key_exists($key, $input)) { 65 | $servoData[$key] = $input[$key]; 66 | } 67 | } 68 | } catch (Exception $e) { 69 | } 70 | 71 | $servo = ''; 72 | $action = $_GET["action"]; 73 | if (array_key_exists($action, $servoData)) { 74 | $action = $servoData[$action]; 75 | } 76 | 77 | switch ($action) { 78 | case 'Xplus': 79 | $servoData['x'] += $servoData['XStep']; 80 | $servoData['x'] = min($servoData['x'], $servoData['XMax']); 81 | $servo = '1=' . $servoData['x'] . "\n"; 82 | break; 83 | case 'Xminus': 84 | $servoData['x'] -= $servoData['XStep']; 85 | $servoData['x'] = max($servoData['x'], $servoData['XMin']); 86 | $servo = '1=' . $servoData['x'] . "\n"; 87 | break; 88 | case 'Yminus': 89 | $servoData['y'] -= $servoData['YStep']; 90 | $servoData['y'] = max($servoData['y'], $servoData['YMin']); 91 | $servo = '0=' . $servoData['y'] . "\n"; 92 | break; 93 | case 'Yplus': 94 | $servoData['y'] += $servoData['YStep']; 95 | $servoData['y'] = min($servoData['y'], $servoData['YMax']); 96 | $servo = '0=' . $servoData['y'] . "\n"; 97 | break; 98 | } 99 | if ($servo != '') { 100 | $fs = fopen(SERVO_CMD, "w"); 101 | fwrite($fs, $servo); 102 | fclose($fs); 103 | } 104 | file_put_contents(BASE_DIR . '/' . SERVO_DATA, json_encode($servoData)); 105 | } 106 | 107 | ?> 108 | -------------------------------------------------------------------------------- /www/js/preview.js: -------------------------------------------------------------------------------- 1 | var SUBDIR_CHAR = "@" 2 | var next, prev; 3 | 4 | (function() { 5 | document.onkeyup = function () { 6 | switch (event.keyCode) { 7 | case 39: 8 | if (next) { 9 | load_preview(next); 10 | } 11 | break; 12 | case 37: 13 | if (prev) { 14 | load_preview(prev); 15 | } 16 | break; 17 | } 18 | }; 19 | })(); 20 | 21 | function load_preview(thumbnail) { 22 | var previewDiv = document.getElementById('preview'); 23 | var prevButton = document.getElementsByName('prev')[0]; 24 | var nextButton = document.getElementsByName('next')[0]; 25 | var downloadButton = document.getElementsByName('download1')[0]; 26 | var deleteButton = document.getElementsByName('delete1')[0]; 27 | var convertButton = document.getElementsByName('convert')[0]; 28 | var mediaDiv = document.getElementById('media'); 29 | var title = document.getElementById('media-title'); 30 | 31 | previewDiv.style.display = 'block'; 32 | history.pushState(null, null, linksBase + thumbnail); 33 | 34 | title.innerHTML = fileTitle(thumbnail); 35 | downloadButton.value = thumbnail; 36 | deleteButton.value = thumbnail; 37 | 38 | var imageIndex = thumbnails.indexOf(thumbnail); 39 | 40 | // Previous 41 | if (imageIndex > 0) { 42 | prev = thumbnails[imageIndex-1]; 43 | prevButton.disabled = false; 44 | prevButton.onclick = function() { 45 | load_preview(prev); 46 | } 47 | 48 | if (fileExtension(imageFromThumbnail(prev)) == 'jpg') { 49 | preloadImage(mediaBase + imageFromThumbnail(prev)); 50 | } 51 | } else { 52 | prev = null; 53 | prevButton.disabled = true; 54 | } 55 | 56 | // Next 57 | if (imageIndex >= 0 && imageIndex < thumbnails.length-1) { 58 | next = thumbnails[imageIndex+1]; 59 | nextButton.disabled = false; 60 | nextButton.onclick = function() { 61 | load_preview(next); 62 | } 63 | 64 | if (fileExtension(imageFromThumbnail(next)) == 'jpg') { 65 | preloadImage(mediaBase + imageFromThumbnail(next)); 66 | } 67 | } else { 68 | next = null; 69 | nextButton.disabled = true; 70 | } 71 | 72 | var mediaURL = mediaBase + imageFromThumbnail(thumbnail); 73 | if (mediaURL) { 74 | var media_content; 75 | if (fileExtension(mediaURL) == 'jpg') { 76 | media_content = ''; 77 | } else { 78 | media_content = ''; 79 | } 80 | 81 | mediaDiv.innerHTML = media_content; 82 | } 83 | 84 | if (fileType(thumbnail) == 't') { 85 | convertButton.style.display = 'inline'; 86 | convertButton.value = thumbnail; 87 | } else { 88 | convertButton.style.display = 'none'; 89 | convertButton.value = ''; 90 | } 91 | } 92 | 93 | function suffixLength(thumbnail) { 94 | return thumbnail.length - thumbnail.lastIndexOf(".", thumbnail.length - 8); 95 | } 96 | 97 | function imageFromThumbnail(thumbnailName) { 98 | var temp = thumbnailName.substr(0, thumbnailName.length - suffixLength(thumbnailName)); 99 | return temp.split(SUBDIR_CHAR).join("/"); 100 | } 101 | 102 | function fileExtension(fileName) { 103 | return fileName.split('.').pop(); 104 | } 105 | 106 | function fileType(fileName) { 107 | var suffix = fileName.substr(-suffixLength(fileName)); 108 | return suffix.substr(1, 1); 109 | } 110 | 111 | function fileTitle(thumbnailName) { 112 | return thumbnailName.substr(thumbnailName.length - suffixLength(thumbnailName) + 1).substr(0, suffixLength(thumbnailName) - 8); 113 | } 114 | 115 | function preloadImage(url) { 116 | var _img = new Image(); 117 | _img.src = url; 118 | } 119 | 120 | function getParameterByName(name, url) { 121 | if (!url) url = window.location.href; 122 | name = name.replace(/[\[\]]/g, "\\$&"); 123 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 124 | results = regex.exec(url); 125 | if (!results) return null; 126 | if (!results[2]) return ''; 127 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 128 | } 129 | -------------------------------------------------------------------------------- /www/js/pipan.js: -------------------------------------------------------------------------------- 1 | // mode=1 pipan, 2=servoblaster 2 | var mode = 0; 3 | 4 | var pan = 100; 5 | var tilt = 100; 6 | var cmd = ""; 7 | var pan_bak = 100; 8 | var tilt_bak = 100; 9 | var pan_start; 10 | var tilt_start; 11 | var touch = false; 12 | var led_stat = false; 13 | var ajax_pipan; 14 | var pipan_mouse_x; 15 | var pipan_mouse_y; 16 | 17 | document.onkeypress = pipan_onkeypress; 18 | 19 | if(window.XMLHttpRequest) { 20 | ajax_pipan = new XMLHttpRequest(); 21 | } 22 | else { 23 | ajax_pipan = new ActiveXObject("Microsoft.XMLHTTP"); 24 | } 25 | ajax_pipan.onreadystatechange = ajax_pipan_done; 26 | 27 | function ajax_pipan_done() { 28 | if(ajax_pipan.readyState == 4) { 29 | if(touch) { 30 | if((pan_bak != pan) || (tilt_bak != tilt)) { 31 | ajax_pipan_start(); 32 | } 33 | else { 34 | setTimeout("ajax_pipan_done()", 100); 35 | } 36 | } 37 | } 38 | } 39 | 40 | function ajax_pipan_start () { 41 | if (mode == 1) 42 | ajax_pipan.open("GET","pipan.php?pan=" + pan + "&tilt=" + tilt, true); 43 | else if (mode == 2) 44 | ajax_pipan.open("GET","pipan.php?action=" + cmd, true); 45 | 46 | if (mode != 0) 47 | ajax_pipan.send(); 48 | 49 | pan_bak = pan; 50 | tilt_bak = tilt; 51 | } 52 | 53 | function servo_left () { 54 | if(pan <= 190) pan += 10; 55 | cmd = "left"; 56 | ajax_pipan_start(); 57 | document.getElementById("arrowLeft").style.borderRight="40px solid red"; 58 | window.setTimeout(clear_arrow_left, 100) 59 | } 60 | function clear_arrow_left() { 61 | document.getElementById("arrowLeft").style.borderRight="40px solid #428bca"; 62 | } 63 | function servo_right () { 64 | if(pan >= 10) pan -= 10; 65 | cmd = "right"; 66 | ajax_pipan_start(); 67 | document.getElementById("arrowRight").style.borderLeft="40px solid red"; 68 | window.setTimeout(clear_arrow_right,100); 69 | } 70 | function clear_arrow_right() { 71 | document.getElementById("arrowRight").style.borderLeft="40px solid #428bca"; 72 | } 73 | 74 | function servo_up () { 75 | if(tilt >= 10) tilt -= 10; 76 | cmd = "up"; 77 | ajax_pipan_start(); 78 | document.getElementById("arrowUp").style.borderBottom="40px solid red"; 79 | window.setTimeout(clear_arrow_up,100); 80 | } 81 | function clear_arrow_up() { 82 | document.getElementById("arrowUp").style.borderBottom="40px solid #428bca"; 83 | } 84 | 85 | function servo_down () { 86 | if(tilt <= 190) tilt += 10; 87 | cmd = "down"; 88 | ajax_pipan_start(); 89 | document.getElementById("arrowDown").style.borderTop="40px solid red"; 90 | window.setTimeout(clear_arrow_down,100); 91 | } 92 | function clear_arrow_down() { 93 | document.getElementById("arrowDown").style.borderTop="40px solid #428bca"; 94 | } 95 | 96 | function led_switch () { 97 | 98 | if(!led_stat) { 99 | led_stat = true; 100 | ajax_pipan.open("GET","pipan.php?red=" + document.getElementById("pilight_r").value + "&green=" + document.getElementById("pilight_g").value + "&blue=" + document.getElementById("pilight_b").value, true); 101 | ajax_pipan.send(); 102 | } 103 | else { 104 | led_stat = false; 105 | ajax_pipan.open("GET","pipan.php?red=0&green=0&blue=0", true); 106 | ajax_pipan.send(); 107 | } 108 | 109 | } 110 | 111 | function pipan_onkeypress (e) { 112 | 113 | if(e.keyCode == 97) servo_left(); 114 | else if(e.keyCode == 119) servo_up(); 115 | else if(e.keyCode == 100) servo_right(); 116 | else if(e.keyCode == 115) servo_down(); 117 | else if(e.keyCode == 102) led_switch(); 118 | 119 | } 120 | 121 | function pipan_start () { 122 | 123 | pipan_mouse_x = null; 124 | pipan_mouse_y = null; 125 | pan_start = pan; 126 | tilt_start = tilt; 127 | document.body.addEventListener('touchmove', pipan_move, false) 128 | document.body.addEventListener('touchend', pipan_stop, false) 129 | touch = true; 130 | ajax_pipan_start(); 131 | 132 | } 133 | 134 | function pipan_move (e) { 135 | 136 | var ev = e || window.event; 137 | 138 | if(pipan_mouse_x == null) { 139 | pipan_mouse_x = e.changedTouches[0].pageX; 140 | pipan_mouse_y = e.changedTouches[0].pageY; 141 | } 142 | mouse_x = e.changedTouches[0].pageX; 143 | mouse_y = e.changedTouches[0].pageY; 144 | e.preventDefault() 145 | 146 | var pan_temp = pan_start + Math.round((mouse_x-pipan_mouse_x)/5); 147 | var tilt_temp = tilt_start + Math.round((pipan_mouse_y-mouse_y)/5); 148 | if(pan_temp > 200) pan_temp = 200; 149 | if(pan_temp < 0) pan_temp = 0; 150 | if(tilt_temp > 200) tilt_temp = 200; 151 | if(tilt_temp < 0) tilt_temp = 0; 152 | 153 | pan = pan_temp; 154 | tilt = tilt_temp; 155 | 156 | } 157 | 158 | 159 | function pipan_stop () { 160 | 161 | document.body.removeEventListener('touchmove', pipan_move, false) 162 | document.body.removeEventListener('touchend', pipan_stop, false) 163 | touch = false; 164 | 165 | } 166 | 167 | function init_pt(p,t) { 168 | pan = p; 169 | tilt = t; 170 | } 171 | 172 | function set_panmode(m) { 173 | mode = m; 174 | } 175 | 176 | -------------------------------------------------------------------------------- /etc/raspimjpeg/raspimjpeg.1: -------------------------------------------------------------------------------- 1 | ################################ 2 | # Config File for raspimjpeg # 3 | ################################ 4 | # Syntax: "Command Param", no spaces before/after line allowed 5 | 6 | # 7 | # Camera Options 8 | # 9 | # annotation max length: 31 characters 10 | annotation RPi Cam %Y.%M.%D_%h:%m:%s 11 | anno_background false 12 | anno3_custom_background_colour 0 13 | anno3_custom_background_Y 0 14 | anno3_custom_background_U 128 15 | anno3_custom_background_V 128 16 | anno3_custom_text_colour 0 17 | anno3_custom_text_Y 255 18 | anno3_custom_text_U 128 19 | anno3_custom_text_V 128 20 | anno_text_size 50 21 | 22 | sharpness 0 23 | contrast 0 24 | brightness 50 25 | saturation 0 26 | iso 0 27 | metering_mode average 28 | video_stabilisation false 29 | exposure_compensation 0 30 | exposure_mode auto 31 | white_balance auto 32 | autowbgain_r 150 33 | autowbgain_b 150 34 | image_effect none 35 | colour_effect_en false 36 | colour_effect_u 128 37 | colour_effect_v 128 38 | rotation 0 39 | hflip false 40 | vflip false 41 | sensor_region_x 0 42 | sensor_region_y 0 43 | sensor_region_w 65536 44 | sensor_region_h 65536 45 | shutter_speed 0 46 | raw_layer false 47 | stat_pass 0 48 | 49 | # camera_num 0 - no selection. 1/2 selects first or second camera on compute module 50 | camera_num 0 51 | 52 | hdmi_preview 0 53 | 54 | #MMAL settings 55 | minimise_frag 0 56 | initial_quant 25 57 | encode_qp 31 58 | #mmal_logfile used for debugging callbacks (set to /dev/shm/mjpeg/mmallogfile for short periods only) 59 | mmal_logfile 60 | #sleep after stopping uSec 61 | stop_pause 100000 62 | 63 | # 64 | # Preview Options 65 | # 66 | # fps_preview = video_fps (below) / divider 67 | # 68 | width 512 69 | quality 10 70 | divider 1 71 | 72 | # 73 | # Video Options 74 | # 75 | video_width 1920 76 | video_height 1080 77 | video_fps 25 78 | fps_divider 1 79 | video_bitrate 17000000 80 | video_buffer 0 81 | #h264_buffer_size 0 sets to default (65536) Higher gives smoother set of callbacks 82 | h264_buffer_size 131072 83 | h264_buffers 0 84 | video_split 0 85 | 86 | #MP4Box Off=leave as raw h264, background=box in background 87 | MP4Box background 88 | MP4Box_fps 25 89 | #MP4Box_cmd (set -e;MP4Box -fps %i -add %s %s > /dev/null 2>&1;rm "%s";) & 90 | MP4Box_cmd (set -e;FPS=%i;TNAME='%s';FNAME='%s';TNAME='%s';LOGS="$TNAME.log";rm -f "$FNAME";if MP4Box -fps $FPS -add "$TNAME" "$FNAME" > "$LOGS" 2>&1;then touch -r "$TNAME" "$FNAME"; rm "$TNAME" "$LOGS";else mv "$TNAME" "$TNAME.bad";fi;) & 91 | # 92 | # Image Options 93 | # 94 | image_width 2592 95 | image_height 1944 96 | image_quality 10 97 | 98 | #time lapse interval 0.1 sec units 99 | tl_interval 30 100 | 101 | # 102 | # Motion Detection 103 | # 104 | motion_external false 105 | vector_preview false 106 | vector_mode ? 107 | motion_noise 1010 108 | motion_threshold 250 109 | motion_clip 0 110 | motion_image 111 | motion_initframes 0 112 | motion_startframes 3 113 | motion_stopframes 150 114 | motion_pipe /var/www/FIFO1 115 | motion_file 0 116 | 117 | # 118 | # File Locations 119 | # 120 | # preview path: add %d for number 121 | # image+video path: add %d for number, year, month, day, hour, minute, second 122 | # macros_path can be used to store macros executed by sy command 123 | # boxing_path if set is where h264 files will be temporarily stored when boxing used 124 | # image, video and lapse may be configured relative to media_path if first / left out 125 | base_path /var/www 126 | preview_path /dev/shm/mjpeg/cam.jpg 127 | image_path /var/www/media/im_%i_%Y%M%D_%h%m%s.jpg 128 | lapse_path /var/www/media/tl_%i_%t_%Y%M%D_%h%m%s.jpg 129 | video_path /var/www/media/vi_%v_%Y%M%D_%h%m%s.mp4 130 | status_file /dev/shm/mjpeg/status_mjpeg.txt 131 | control_file /var/www/FIFO 132 | media_path /var/www/media 133 | macros_path /var/www/macros 134 | user_annotate /dev/shm/mjpeg/user_annotate.txt 135 | boxing_path 136 | subdir_char @ 137 | count_format %04d 138 | 139 | #Job macros - prefix with & to make it run asynchronously 140 | error_soft error_soft.sh 141 | error_hard error_hard.sh 142 | start_img start_img.sh 143 | end_img &end_img.sh 144 | start_vid &start_vid.sh 145 | end_vid end_vid.sh 146 | end_box &end_box.sh 147 | do_cmd &do_cmd.sh 148 | motion_event motion_event.sh 149 | startstop startstop.sh 150 | 151 | # thumb generator control 152 | # Set v, i, or t in string to enable thumbs for images, videos, or lapse 153 | thumb_gen vit 154 | 155 | # 156 | # Autostart 157 | # 158 | # autostart: standard/idle 159 | # motion detection can only be true if autostart is standard 160 | # 161 | autostart standard 162 | motion_detection false 163 | 164 | # Watchdog 165 | # Interval in 0.1 secs 166 | # Errors is Number of times cam.jpg doesn't change before exit 167 | watchdog_interval 30 168 | watchdog_errors 3 169 | # Set callback_timeout to 0 to disable it 170 | callback_timeout 30 171 | #optional user_config file to overwrite (persist) changes 172 | user_config /var/www/uconfig 173 | 174 | #logfile for raspimjpeg, default to merge with scheduler log 175 | log_file /var/www/scheduleLog.txt 176 | log_size 5000 177 | motion_logfile /var/www/motionLog.txt 178 | 179 | #enforce_lf set to 1 to only process FIFO commands when terminated with LF 180 | enforce_lf 0 181 | 182 | #FIFO poll interval microseconds 1000000 minimum 183 | fifo_interval 100000 184 | 185 | -------------------------------------------------------------------------------- /remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2015, Bob Tidey 4 | # All rights reserved. 5 | 6 | # Redistribution and use, with or without modification, are permitted provided 7 | # that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Neither the name of the copyright holder nor the 11 | # names of its contributors may be used to endorse or promote products 12 | # derived from this software without specific prior written permission. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 18 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | # Description 26 | # This script removes the browser-interface to control the RPi Cam. It can be run 27 | # on any Raspberry Pi with a newly installed raspbian and enabled camera-support. 28 | # Based on RPI_Cam_WEb_Interface installer by Silvan Melchior 29 | # Edited by jfarcher to work with github 30 | # Edited by slabua to support custom installation folder 31 | # Additions by btidey, miraaz, gigpi 32 | # Split up and refactored by Bob Tidey 33 | 34 | #Debug enable next 3 lines 35 | exec 5> remove.txt 36 | BASH_XTRACEFD="5" 37 | set -x 38 | 39 | 40 | cd $(dirname $(readlink -f $0)) 41 | 42 | # Terminal colors 43 | color_red="tput setaf 1" 44 | color_green="tput setaf 2" 45 | color_reset="tput sgr0" 46 | 47 | source ./config.txt 48 | 49 | fn_stop () 50 | { # This is function stop 51 | sudo killall raspimjpeg 2>/dev/null 52 | sudo killall php 2>/dev/null 53 | sudo killall motion 2>/dev/null 54 | sudo service apache2 stop >dev/null 2>&1 55 | sudo service nginx stop >dev/null 2>&1 56 | sudo service lighttpd stop >dev/null 2>&1 57 | dialog --title 'Stop message' --infobox 'Stopped.' 4 16 ; sleep 2 58 | } 59 | 60 | fn_autostart_disable () 61 | { 62 | tmpfile=$(mktemp) 63 | sudo sed '/#START/,/#END/d' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 64 | # Remove to growing plank lines. 65 | sudo awk '!NF {if (++n <= 1) print; next}; {n=0;print}' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 66 | 67 | # Finally we set owners and permissions all files what we changed. 68 | sudo chown root:root /etc/rc.local 69 | sudo chmod 755 /etc/rc.local 70 | sudo chmod 664 ./config.txt 71 | } 72 | 73 | 74 | fn_apache_default () 75 | { 76 | if [ -e /etc/apache2/sites-available/000-default.conf ]; then 77 | adefault="/etc/apache2/sites-available/000-default.conf" 78 | subdir="\/html" 79 | else 80 | adefault="/etc/apache2/sites-available/default" 81 | subdir="" 82 | fi 83 | tmpfile=$(mktemp) 84 | webport="80" 85 | user="" 86 | passwd="" 87 | sudo awk '/NameVirtualHost \*:/{c+=1}{if(c==1){sub("NameVirtualHost \\*:.*","NameVirtualHost *:'$webport'",$0)};print}' /etc/apache2/ports.conf > "$tmpfile" && sudo mv "$tmpfile" /etc/apache2/ports.conf 88 | sudo awk '/Listen/{c+=1}{if(c==1){sub("Listen.*","Listen '$webport'",$0)};print}' /etc/apache2/ports.conf > "$tmpfile" && sudo mv "$tmpfile" /etc/apache2/ports.conf 89 | sudo awk '/",$0)};print}' $adefault > "$tmpfile" && sudo mv "$tmpfile" $adefault 90 | sudo sed -i "s/DocumentRoot\ \/var\/www\/.*/DocumentRoot\ \/var\/www$subdir/g" $adefault 91 | sudo awk '/AllowOverride/{c+=1}{if(c==2){sub("AllowOverride.*","AllowOverride None",$0)};print}' /etc/apache2/sites-available/default > "$tmpfile" && sudo mv "$tmpfile" $adefault 92 | sudo service apache2 restart 93 | } 94 | 95 | 96 | fn_stop 97 | 98 | dialog --title "Uninstall packages!" --backtitle "$backtitle" --yesno "Do You want uninstall webserver and php packages also?" 6 35 99 | response=$? 100 | case $response in 101 | 0) package=('apache2' 'php5' 'libapache2-mod-php5' 'php5-cli' 'zip' 'nginx' 'lighttpd' 'apache2-utils' 'php5-fpm' 'php5-common' 'php-apc' 'gpac' 'motion' 'libav-tools');; 102 | 1) package=('zip' 'gpac motion' 'libav-tools');; 103 | 255) dialog --title 'Uninstall message' --infobox 'Webserver and php packages not uninstalled.' 4 33 ; sleep 2;; 104 | esac 105 | for i in "${package[@]}" 106 | do 107 | if [ $(dpkg-query -W -f='${Status}' "$i" 2>/dev/null | grep -c "ok installed") -eq 1 ]; then 108 | sudo apt-get remove -y "$i" 109 | fi 110 | done 111 | sudo apt-get autoremove -y 112 | 113 | if [ ! -d ~/media ]; then 114 | mkdir ~/media 115 | fi 116 | if [ ! "$rpicamdir" == "" ]; then 117 | sudo mv /var/www/$rpicamdir/media ~/media 118 | sudo rm -r /var/www/$rpicamdir 119 | else 120 | sudo mv /var/www/media ~/media 121 | sudo rm /var/www/* 122 | fi 123 | sudo rm /etc/sudoers.d/RPI_Cam_Web_Interface 124 | sudo rm /usr/bin/raspimjpeg 125 | sudo rm /etc/raspimjpeg 126 | fn_autostart_disable 127 | 128 | sudo mv etc/nginx/sites-available/*default* /etc/nginx/sites-available >/dev/null 2>&1 129 | sudo mv etc/apache2/sites-available/*default* /etc/apache2/sites-available >/dev/null 2>&1 130 | 131 | if [ $(dpkg-query -W -f='${Status}' "apache2" 2>/dev/null | grep -c "ok installed") -eq 1 ]; then 132 | fn_apache_default 133 | fi 134 | 135 | -------------------------------------------------------------------------------- /www/motion.php: -------------------------------------------------------------------------------- 1 | 2 | $value) { 34 | if (array_key_exists($key, $motionPars)) { 35 | if ($value != $motionPars[$key]) { 36 | setMotionPar($key, $value); 37 | $changed = true; 38 | } 39 | } 40 | } 41 | if ($changed) { 42 | writeMotionPars(); 43 | $motionConfig = restartMotion(); 44 | $motionPars = parse_ini_string($motionConfig, False, INI_SCANNER_RAW); 45 | } 46 | break; 47 | case 'showAll': 48 | $showAll = true; 49 | break; 50 | case 'backup': 51 | $backup = array(); 52 | $backup[MOTION_PARS] = $motionPars; 53 | $fp = fopen(MOTION_CONFIGBACKUP, 'w'); 54 | fwrite($fp, json_encode($backup)); 55 | fclose($fp); 56 | break; 57 | case 'restore': 58 | if (file_exists(MOTION_CONFIGBACKUP)) { 59 | $restore = json_decode(file_get_contents(MOTION_CONFIGBACKUP)); 60 | $motionPars = $restore[MOTION_PARS]; 61 | foreach ($motionPars as $mKey => $mValue) { 62 | setMotionPar($mKey, $mValue); 63 | } 64 | writeMotionPars(); 65 | restartMotion(); 66 | } 67 | break; 68 | } 69 | } 70 | } 71 | 72 | function checkMotion() { 73 | $pids = array(); 74 | exec("pgrep motion", $pids); 75 | if (empty($pids)) { 76 | return false; 77 | } else { 78 | return true; 79 | } 80 | } 81 | 82 | function setMotionPar($k, $v) { 83 | global $debugString; 84 | 85 | $t = file_get_contents(MOTION_URL . "config/set?" . $k ."=" . urlencode($v)); 86 | } 87 | 88 | function writeMotionPars() { 89 | $t = file_get_contents(MOTION_URL . "config/write"); 90 | } 91 | 92 | function pauseMotion() { 93 | $t = file_get_contents(MOTION_URL . "detection/pause"); 94 | } 95 | 96 | function startMotion() { 97 | $t = file_get_contents(MOTION_URL . "detection/start"); 98 | } 99 | 100 | //restart and fetch updated config list 101 | function restartMotion() { 102 | $t = file_get_contents(MOTION_URL . "action/restart"); 103 | $retry = 20; 104 | do { 105 | sleep(1); 106 | $t = file_get_contents(MOTION_URL . "config/list"); 107 | if ($t) { 108 | return $t; 109 | } 110 | $retry--; 111 | } while ($retry > 0); 112 | } 113 | 114 | function buildParsTable($pars, $fPars, $f) { 115 | echo ''; 116 | foreach ($pars as $mKey => $mValue) { 117 | if ($f || in_array($mKey, $fPars)) { 118 | echo ""; 119 | } 120 | } 121 | echo "
$mKey
"; 122 | } 123 | ?> 124 | 125 | 126 | 127 | RPi Cam Download 128 | 129 | 130 | 131 | 132 | 133 | 134 | 141 | 142 |
143 |
144 | "; 146 | echo '
'; 147 | if ($showAll) { 148 | echo ""; 149 | } else { 150 | echo ""; 151 | } 152 | echo "  "; 153 | echo "  "; 154 | echo "  

"; 155 | echo '
'; 156 | if ($motionReady) { 157 | buildParsTable($motionPars, $filterPars, $showAll); 158 | } else { 159 | echo "

Motion not running. Put in detection state

"; 160 | } 161 | ?> 162 |
163 |
164 | 165 | 166 | -------------------------------------------------------------------------------- /www/config.php: -------------------------------------------------------------------------------- 1 | 0) { 66 | $log = fopen(getLogFile(), 'a'); 67 | $time = date('[Y/m/d H:i:s]'); 68 | fwrite($log, "$time $msg" . PHP_EOL); 69 | fclose($log); 70 | } 71 | } 72 | 73 | // functions to read and save config data 74 | function readConfigs() { 75 | global $config, $logFile, $logSize; 76 | $config = readConfig($config, LBASE_DIR . '/' . CONFIG_FILE1); 77 | $config = readConfig($config, LBASE_DIR . '/' . CONFIG_FILE2); 78 | $logFile = $config['log_file']; 79 | $logSize = $config['log_size']; 80 | } 81 | 82 | function readConfig($config, $configFile) { 83 | if (file_exists($configFile)) { 84 | $lines = array(); 85 | $data = file_get_contents($configFile); 86 | $lines = explode("\n", $data); 87 | foreach($lines as $line) { 88 | if (strlen($line) && substr($line, 0, 1) != '#') { 89 | $index = strpos($line, ' '); 90 | if ($index !== false) { 91 | $key = substr($line, 0, $index); 92 | $value = substr($line, $index +1); 93 | if ($value == 'true') $value = 1; 94 | if ($value == 'false') $value = 0; 95 | $config[$key] = $value; 96 | } else { 97 | $config[$line] = ""; 98 | } 99 | } 100 | } 101 | } 102 | return $config; 103 | } 104 | 105 | function saveUserConfig($config) { 106 | $cstring= ""; 107 | foreach($config as $key => $value) { 108 | $cstring .= $key . ' ' . $value . "\n"; 109 | } 110 | if ($cstring != "") { 111 | $fp = fopen(LBASE_DIR . '/' . CONFIG_FILE2, 'w'); 112 | fwrite($fp, "#User config file\n"); 113 | fwrite($fp, $cstring); 114 | fclose($fp); 115 | } 116 | } 117 | 118 | // functions to find and delete data files 119 | 120 | function getSortedFiles($ascending = true) { 121 | $scanfiles = scandir(LBASE_DIR . '/' . MEDIA_PATH); 122 | $files = array(); 123 | foreach($scanfiles as $file) { 124 | if(($file != '.') && ($file != '..') && isThumbnail($file)) { 125 | $fDate = filemtime(LBASE_DIR . '/' . MEDIA_PATH . "/$file"); 126 | $files[$file] = $fDate; 127 | } 128 | } 129 | if ($ascending) 130 | asort($files); 131 | else 132 | arsort($files); 133 | return array_keys($files); 134 | } 135 | 136 | function findLapseFiles($d) { 137 | //return an arranged in time order and then must have a matching 4 digit batch and an incrementing lapse number 138 | $batch = getFileIndex($d); 139 | $padlen = strlen($batch); 140 | $fullname = LBASE_DIR . '/' . MEDIA_PATH . '/' . dataFilename($d); 141 | $path = dirname($fullname); 142 | $start = filemtime("$fullname"); 143 | $files = array(); 144 | $scanfiles = scandir($path); 145 | $lapsefiles = array(); 146 | foreach($scanfiles as $file) { 147 | if (strpos($file, $batch) !== false) { 148 | if (!isThumbnail($file) && strcasecmp(fileext($file), "jpg") == 0) { 149 | $fDate = filemtime("$path/$file"); 150 | if ($fDate >= $start) { 151 | $files[$file] = $fDate . $file; 152 | } 153 | } 154 | } 155 | } 156 | asort($files); 157 | $lapseCount = 1; 158 | foreach($files as $key => $value) { 159 | if (strpos($key, str_pad($lapseCount, $padlen, 0, STR_PAD_LEFT)) !== false) { 160 | $lapsefiles[] = "$path/$key"; 161 | $lapseCount++; 162 | } else { 163 | break; 164 | } 165 | } 166 | return $lapsefiles; 167 | } 168 | 169 | //function to get filesize (native php has 2GB limit) 170 | function filesize_n($path) { 171 | if (FILESIZE_METHOD == '0') { 172 | $size = filesize($path); 173 | if ($size > 0) 174 | return $size; 175 | else 176 | return 4294967296 - $size; 177 | } else { 178 | return trim(`stat -c%s $path`); 179 | } 180 | } 181 | 182 | //function to delete all files associated with a thumb name 183 | //returns space freed in kB 184 | //if $del = false just calculate space which would be freed 185 | function deleteFile($d, $del = true) { 186 | $size = 0; 187 | if(!is_writeable(LBASE_DIR . '/' . MEDIA_PATH . "/$d")) return 0; 188 | $t = getFileType($d); 189 | if ($t == 't') { 190 | // For time lapse try to delete all from this batch 191 | $files = findLapseFiles($d); 192 | foreach($files as $file) { 193 | $size += filesize_n($file); 194 | if ($del) if(!unlink($file)) $debugString .= "F "; 195 | } 196 | } else { 197 | $tFile = dataFilename($d); 198 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile")) { 199 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile"); 200 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile"); 201 | } 202 | if ($t == 'v') { 203 | $rFile = substr($tFile,0, strrpos($tFile, '.')); 204 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.dat")) { 205 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.dat"); 206 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.dat"); 207 | } 208 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264")) { 209 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264"); 210 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264"); 211 | } 212 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.bad")) { 213 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.bad"); 214 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.bad"); 215 | } 216 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.log")) { 217 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.log"); 218 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$rFile.h264.log"); 219 | } 220 | } 221 | } 222 | $size += filesize_n(LBASE_DIR . '/' . MEDIA_PATH . "/$d"); 223 | if ($del) unlink(LBASE_DIR . '/' . MEDIA_PATH . "/$d"); 224 | return $size / 1024; 225 | } 226 | 227 | //function to lock or unlock all files associated with a thumb name 228 | function lockFile($d, $lock) { 229 | if($lock == 1) $attr = 0444; else $attr = 0644; 230 | $t = getFileType($d); 231 | if ($t == 't') { 232 | // For time lapse lock all from this batch 233 | $files = findLapseFiles($d); 234 | foreach($files as $file) { 235 | chmod($file, $attr); 236 | } 237 | } else { 238 | $tFile = dataFilename($d); 239 | if (file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile")) { 240 | chmod(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile", $attr); 241 | } 242 | if ($t == 'v' && file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.dat")) { 243 | chmod(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.dat", $attr); 244 | } 245 | if ($t == 'v' && file_exists(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.h264")) { 246 | chmod(LBASE_DIR . '/' . MEDIA_PATH . "/$tFile.h264", $attr); 247 | } 248 | } 249 | chmod(LBASE_DIR . '/' . MEDIA_PATH . "/$d", $attr); 250 | } 251 | 252 | 253 | //Support naming functions 254 | function dataFilename($file) { 255 | $i = strrpos($file, '.', -8); 256 | if ($i !== false) 257 | return str_replace(SUBDIR_CHAR, '/', substr($file, 0, $i)); 258 | else 259 | return ""; 260 | } 261 | 262 | function dataFileext($file) { 263 | $f = dataFileName($file); 264 | return fileext($f); 265 | } 266 | 267 | function fileext($f) { 268 | if ($f <> "") { 269 | $i = strrpos($f, '.'); 270 | if ($i !== false) 271 | return substr($f, $i+1); 272 | } 273 | return ""; 274 | } 275 | 276 | function isThumbnail($file) { 277 | return (substr($file, -7) == THUMBNAIL_EXT); 278 | } 279 | 280 | function getFileType($file) { 281 | $i = strrpos($file, '.', -8); 282 | if ($i !== false) 283 | return substr($file, $i + 1, 1); 284 | else 285 | return ""; 286 | } 287 | 288 | function getFileIndex($file) { 289 | $i = strrpos($file, '.', -8); 290 | if ($i !== false) 291 | return substr($file, $i + 2, strlen($file) - $i - 9); 292 | else 293 | return ""; 294 | } 295 | 296 | function getLogFile() { 297 | global $logFile; 298 | if ($logFile != "") 299 | return $logFile; 300 | else 301 | return LBASE_DIR . '/' . LOGFILE_SCHEDULE; 302 | } 303 | 304 | function getLogSize() { 305 | global $logSize; 306 | readConfigs(); 307 | return $logSize; 308 | } 309 | 310 | function getStyle() { 311 | return 'css/' . file_get_contents(BASE_DIR . '/css/extrastyle.txt'); 312 | } 313 | 314 | function getUser() { 315 | $serverSoftware = $_SERVER['SERVER_SOFTWARE']; 316 | if(stripos($serverSoftware, 'apache') !== false) { 317 | $user = apache_getenv("REMOTE_USER"); 318 | } else if(stripos($serverSoftware, 'nginx') !== false) { 319 | try { 320 | $user = $_SERVER['REMOTE_USER']; 321 | } catch (Exception $e) { 322 | $user = ''; 323 | } 324 | } else { 325 | $user = ''; 326 | } 327 | return $user; 328 | } 329 | 330 | function getUserLevel($user) { 331 | global $userLevels, $userInit; 332 | if(strlen($user) == 0) { 333 | return USERLEVEL_MAX; 334 | } else if (file_exists(LBASE_DIR . '/' . USERLEVEL_FILE)) { 335 | //Initialise userLevels array if possible 336 | if($userInit == 0) { 337 | $userInit = 1; 338 | $lines = array(); 339 | $data = file_get_contents(LBASE_DIR . '/' . USERLEVEL_FILE); 340 | $lines = explode("\n", $data); 341 | foreach($lines as $line) { 342 | if (strlen($line)) { 343 | $index = strpos($line, ':'); 344 | if ($index !== false) { 345 | $userLevels[substr($line, 0, $index)] = substr($line, $index +1); 346 | } 347 | } 348 | } 349 | } 350 | if(array_key_exists($user, $userLevels)) 351 | return $userLevels[$user]; 352 | else 353 | return USERLEVEL_MIN; 354 | } else 355 | return USERLEVEL_MAX; 356 | } 357 | 358 | $config = array(); 359 | $userLevels = array(); 360 | $userInit = 0; 361 | $logFile = ""; 362 | $logSize = 1; 363 | readConfigs(); 364 | ?> 365 | -------------------------------------------------------------------------------- /www/js/script.js: -------------------------------------------------------------------------------- 1 | // 2 | // Interface 3 | // 4 | var elem = document.documentElement; 5 | function openFullscreen() { 6 | if (elem.requestFullscreen) { 7 | elem.requestFullscreen(); 8 | } else if (elem.webkitRequestFullscreen) { /* Safari */ 9 | elem.webkitRequestFullscreen(); 10 | } else if (elem.msRequestFullscreen) { /* IE11 */ 11 | elem.msRequestFullscreen(); 12 | } 13 | } 14 | 15 | function closeFullscreen() { 16 | if (document.exitFullscreen) { 17 | document.exitFullscreen(); 18 | } else if (document.webkitExitFullscreen) { /* Safari */ 19 | document.webkitExitFullscreen(); 20 | } else if (document.msExitFullscreen) { /* IE11 */ 21 | document.msExitFullscreen(); 22 | } 23 | } 24 | 25 | function toggle_fullscreen(e) { 26 | 27 | var background = document.getElementById("background"); 28 | 29 | if(!background) { 30 | background = document.createElement("div"); 31 | background.id = "background"; 32 | document.body.appendChild(background); 33 | } 34 | 35 | if(e.className == "fullscreen") { 36 | e.className = ""; 37 | background.style.display = "none"; 38 | closeFullscreen(); 39 | } 40 | else { 41 | e.className = "fullscreen"; 42 | background.style.display = "block"; 43 | openFullscreen(); 44 | } 45 | 46 | } 47 | 48 | function set_display(value) { 49 | var d = new Date(); 50 | var val; 51 | d.setTime(d.getTime() + (365*24*60*60*1000)); 52 | var expires = "expires="+d.toUTCString(); 53 | if (value == "SimpleOff") { 54 | val = "Off"; 55 | } else if (value == "SimpleOn") { 56 | val = "Full"; 57 | } else { 58 | val = value 59 | } 60 | 61 | document.cookie="display_mode=" + val + "; " + expires; 62 | document.location.reload(true); 63 | } 64 | 65 | function set_stream_mode(value) { 66 | var d = new Date(); 67 | d.setTime(d.getTime() + (365*24*60*60*1000)); 68 | var expires = "expires="+d.toUTCString(); 69 | 70 | if (value == "DefaultStream") { 71 | document.getElementById("toggle_stream").value = "MJPEG-Stream"; 72 | } else { 73 | document.getElementById("toggle_stream").value = "Default-Stream"; 74 | } 75 | document.cookie="stream_mode=" + value + "; " + expires; 76 | document.location.reload(true); 77 | } 78 | 79 | function schedule_rows() { 80 | var sun, day, fixed, mode; 81 | mode = parseInt(document.getElementById("DayMode").value); 82 | switch(mode) { 83 | case 0: sun = 'table-row'; day = 'none'; fixed = 'none'; break; 84 | case 1: sun = 'none'; day = 'table-row'; fixed = 'none'; break; 85 | case 2: sun = 'none'; day = 'none'; fixed = 'table-row'; break; 86 | default: sun = 'table-row'; day = 'table-row'; fixed = 'table-row'; break; 87 | } 88 | var rows; 89 | rows = document.getElementsByClassName('sun'); 90 | for(i=0; i install.txt 36 | BASH_XTRACEFD="5" 37 | set -x 38 | 39 | cd $(dirname $(readlink -f $0)) 40 | 41 | if [ $(dpkg-query -W -f='${Status}' "dialog" 2>/dev/null | grep -c "ok installed") -eq 0 ]; then 42 | sudo apt-get install -y dialog 43 | fi 44 | 45 | #Legacy support Bullseye 46 | read -d . VERSION < /etc/debian_version 47 | echo $VERSION 48 | if [ $VERSION -eq 10 ]; then 49 | phpversion=7.3 50 | elif [ $VERSION -eq 11 ]; then 51 | phpversion=7.4 52 | #following is done by enable legacy camera in updates raspi-config 53 | # sudo sed -i 's/^camera_auto_detect=1/#camera_auto_detect=1/g' /boot/config.txt 54 | # sudo grep -qxF 'start_x=1' /boot/config.txt || sudo sed -i '$ a start_x=1' /boot/config.txt 55 | # sudo grep -qxF 'gpu_mem=128' /boot/config.txt || sudo sed -i '$ a gpu_mem=128' /boot/config.txt 56 | sudo mkdir -p /opt/vc/bin 57 | elif [ $VERSION -eq 12 ]; then 58 | phpversion=8.2 59 | #following is done by enable legacy camera in updates raspi-config 60 | # sudo sed -i 's/^camera_auto_detect=1/#camera_auto_detect=1/g' /boot/config.txt 61 | # sudo grep -qxF 'start_x=1' /boot/config.txt || sudo sed -i '$ a start_x=1' /boot/config.txt 62 | # sudo grep -qxF 'gpu_mem=128' /boot/config.txt || sudo sed -i '$ a gpu_mem=128' /boot/config.txt 63 | sudo mkdir -p /opt/vc/bin 64 | else 65 | phpversion=7.0 66 | fi 67 | 68 | # Terminal colors 69 | color_red="tput setaf 1" 70 | color_green="tput setaf 2" 71 | color_reset="tput sgr0" 72 | 73 | # Version stuff moved out functions as we need it more when one time. 74 | versionfile="./www/config.php" 75 | version=$(cat $versionfile | grep "'APP_VERSION'" | cut -d "'" -f4) 76 | backtitle="Copyright (c) 2015, Bob Tidey. RPi Cam $version" 77 | jpglink="no" 78 | 79 | # Config options located in ./config.txt. In first run script makes that file for you. 80 | if [ ! -e ./config.txt ]; then 81 | sudo echo "#This is config file for main installer. Put any extra options in here." > ./config.txt 82 | sudo echo "rpicamdir=\"html\"" >> ./config.txt 83 | sudo echo "webserver=\"apache\"" >> ./config.txt 84 | sudo echo "webport=\"80\"" >> ./config.txt 85 | sudo echo "user=\"\"" >> ./config.txt 86 | sudo echo "webpasswd=\"\"" >> ./config.txt 87 | sudo echo "autostart=\"yes\"" >> ./config.txt 88 | sudo echo "jpglink=\"no\"" >> ./config.txt 89 | sudo echo "phpversion=\"$phpversion\"" >> ./config.txt 90 | sudo echo "" >> ./config.txt 91 | sudo chmod 664 ./config.txt 92 | fi 93 | 94 | source ./config.txt 95 | rpicamdirold=$rpicamdir 96 | if [ ! "${rpicamdirold:0:1}" == "" ]; then 97 | rpicamdirold=/$rpicamdirold 98 | fi 99 | 100 | 101 | #Allow for a quiet install 102 | rm exitfile.txt >/dev/null 2>&1 103 | if [ $# -eq 0 ] || [ "$1" != "q" ]; then 104 | exec 3>&1 105 | dialog \ 106 | --separate-widget $'\n' \ 107 | --title "Configuration Options" \ 108 | --backtitle "$backtitle" \ 109 | --form "" \ 110 | 0 0 0 \ 111 | "Cam subfolder:" 1 1 "$rpicamdir" 1 32 15 0 \ 112 | "Autostart:(yes/no)" 2 1 "$autostart" 2 32 15 0 \ 113 | "Server:(apache/nginx/lighttpd)" 3 1 "$webserver" 3 32 15 0 \ 114 | "Webport:" 4 1 "$webport" 4 32 15 0 \ 115 | "User:(blank=nologin)" 5 1 "$user" 5 32 15 0 \ 116 | "Password:" 6 1 "$webpasswd" 6 32 15 0 \ 117 | "jpglink:(yes/no)" 7 1 "$jpglink" 7 32 15 0 \ 118 | "php:(stretch 7.0,buster 7.3)" 8 1 "$phpversion" 8 32 15 0 \ 119 | 2>&1 1>&3 | { 120 | read -r rpicamdir 121 | read -r autostart 122 | read -r webserver 123 | read -r webport 124 | read -r user 125 | read -r webpasswd 126 | read -r jpglink 127 | read -r phpversion 128 | if [ -n "$webport" ]; then 129 | sudo echo "#This is edited config file for main installer. Put any extra options in here." > ./config.txt 130 | sudo echo "rpicamdir=\"$rpicamdir\"" >> ./config.txt 131 | sudo echo "webserver=\"$webserver\"" >> ./config.txt 132 | sudo echo "webport=\"$webport\"" >> ./config.txt 133 | sudo echo "user=\"$user\"" >> ./config.txt 134 | sudo echo "webpasswd=\"$webpasswd\"" >> ./config.txt 135 | sudo echo "autostart=\"$autostart\"" >> ./config.txt 136 | sudo echo "jpglink=\"$jpglink\"" >> ./config.txt 137 | sudo echo "phpversion=\"$phpversion\"" >> ./config.txt 138 | sudo echo "" >> ./config.txt 139 | else 140 | echo "exit" > ./exitfile.txt 141 | fi 142 | } 143 | exec 3>&- 144 | 145 | if [ -e exitfile.txt ]; then 146 | rm exitfile.txt 147 | exit 148 | fi 149 | 150 | source ./config.txt 151 | fi 152 | 153 | if [ ! "${rpicamdir:0:1}" == "" ]; then 154 | rpicamdir=/$rpicamdir 155 | rpicamdirEsc=${rpicamdir//\//\\\/} 156 | else 157 | rpicamdirEsc="" 158 | fi 159 | 160 | fn_stop () 161 | { # This is function stop 162 | sudo killall raspimjpeg 2>/dev/null 163 | sudo killall php 2>/dev/null 164 | sudo killall motion 2>/dev/null 165 | } 166 | 167 | fn_reboot () 168 | { # This is function reboot system 169 | dialog --title "Start camera system now" --backtitle "$backtitle" --yesno "Start now?" 5 33 170 | response=$? 171 | case $response in 172 | 0) ./start.sh;; 173 | 1) dialog --title 'Start or Reboot message' --colors --infobox "\Zb\Z1"'Manually run ./start.sh or reboot!' 4 28 ; sleep 2;; 174 | 255) dialog --title 'Start or Reboot message' --colors --infobox "\Zb\Z1"'Manually run ./start.sh or reboot!' 4 28 ; sleep 2;; 175 | esac 176 | } 177 | 178 | 179 | fn_apache () 180 | { 181 | aconf="etc/apache2/sites-available/raspicam.conf" 182 | cp $aconf.1 $aconf 183 | if [ -e "\/$aconf" ]; then 184 | sudo rm "\/$aconf" 185 | fi 186 | if [ -e /etc/apache2/conf-available/other-vhosts-access-log.conf ]; then 187 | aotherlog="/etc/apache2/conf-available/other-vhosts-access-log.conf" 188 | else 189 | aotherlog="/etc/apache2/conf.d/other-vhosts-access-log" 190 | fi 191 | tmpfile=$(mktemp) 192 | sudo awk '/NameVirtualHost \*:/{c+=1}{if(c==1){sub("NameVirtualHost \\*:.*","NameVirtualHost *:'$webport'",$0)};print}' /etc/apache2/ports.conf > "$tmpfile" && sudo mv "$tmpfile" /etc/apache2/ports.conf 193 | sudo awk '/Listen/{c+=1}{if(c==1){sub("Listen.*","Listen '$webport'",$0)};print}' /etc/apache2/ports.conf > "$tmpfile" && sudo mv "$tmpfile" /etc/apache2/ports.conf 194 | sudo awk '/",$0)};print}' $aconf > "$tmpfile" && sudo mv "$tmpfile" $aconf 195 | sudo sed -i "s//g" $aconf 196 | if [ "$user" == "" ]; then 197 | sudo sed -i "s/AllowOverride\ .*/AllowOverride None/g" $aconf 198 | else 199 | if [ ! -e /usr/local/.htpasswd ]; then 200 | sudo htpasswd -b -B -c /usr/local/.htpasswd $user $webpasswd 201 | fi 202 | sudo htpasswd -b -B /usr/local/.htpasswd $user $webpasswd 203 | sudo sed -i "s/AllowOverride\ .*/AllowOverride All/g" $aconf 204 | if [ ! -e /var/www$rpicamdir/.htaccess ]; then 205 | sudo bash -c "cat > /var/www$rpicamdir/.htaccess" << EOF 206 | AuthName "RPi Cam Web Interface Restricted Area" 207 | AuthType Basic 208 | AuthUserFile /usr/local/.htpasswd 209 | Require valid-user 210 | EOF 211 | sudo chown -R www-data:www-data /var/www$rpicamdir/.htaccess 212 | fi 213 | fi 214 | sudo mv $aconf /$aconf 215 | if [ ! -e /etc/apache2/sites-enabled/raspicam.conf ]; then 216 | sudo ln -sf /$aconf /etc/apache2/sites-enabled/raspicam.conf 217 | fi 218 | sudo sed -i 's/^CustomLog/#CustomLog/g' $aotherlog 219 | sudo a2dissite 000-default.conf >/dev/null 2>&1 220 | sudo service apache2 restart 221 | } 222 | 223 | fn_nginx () 224 | { 225 | aconf="etc/nginx/sites-available/rpicam" 226 | cp $aconf.1 $aconf 227 | if [ -e "\/$aconf" ]; then 228 | sudo rm "\/$aconf" 229 | fi 230 | #uncomment next line if wishing to always access by http://ip as the root 231 | #sudo sed -i "s:root /var/www;:root /var/www$rpicamdirEsc;:g" $aconf 232 | sudo mv /etc/nginx/sites-available/*default* etc/nginx/sites-available/ >/dev/null 2>&1 233 | #remove link file as nginx now errors if link invalid 234 | sudo rm /etc/nginx/sites-enabled/*default* >/dev/null 2>&1 235 | 236 | if [ "$user" == "" ]; then 237 | sed -i "s/auth_basic\ .*/auth_basic \"Off\";/g" $aconf 238 | sed -i "s/\ auth_basic_user_file/#auth_basic_user_file/g" $aconf 239 | else 240 | sudo htpasswd -b -c /usr/local/.htpasswd $user $webpasswd 241 | sed -i "s/auth_basic\ .*/auth_basic \"Restricted\";/g" $aconf 242 | sed -i "s/#auth_basic_user_file/\ auth_basic_user_file/g" $aconf 243 | fi 244 | if [[ "$phpversion" == "7.4" ]]; then 245 | sed -i "s/\/var\/run\/php5-fpm\.sock;/\/run\/php\/php7.4-fpm\.sock;/g" $aconf 246 | elif [[ "$phpversion" == "7.3" ]]; then 247 | sed -i "s/\/var\/run\/php5-fpm\.sock;/\/run\/php\/php7.3-fpm\.sock;/g" $aconf 248 | fi 249 | sudo sed -i -E "s/(listen.+?)80/\1$webport/g" $aconf 250 | # following line sets root url to direct subfolder. This is inconsistent with other usage. 251 | #sudo sed -i -E "s/root \/var\/www/root \/var\/www$rpicamdirEsc/" $aconf 252 | sudo mv $aconf /$aconf 253 | sudo chmod 644 /$aconf 254 | if [ ! -e /etc/nginx/sites-enabled/rpicam ]; then 255 | sudo ln -sf /$aconf /etc/nginx/sites-enabled/rpicam 256 | fi 257 | 258 | # Update nginx main config file 259 | sudo sed -i "s/worker_processes 4;/worker_processes 2;/g" /etc/nginx/nginx.conf 260 | sudo sed -i "s/worker_connections 768;/worker_connections 128;/g" /etc/nginx/nginx.conf 261 | sudo sed -i "s/gzip on;/gzip off;/g" /etc/nginx/nginx.conf 262 | if [ "$NGINX_DISABLE_LOGGING" != "" ]; then 263 | sudo sed -i "s:access_log /var/log/nginx/nginx/access.log;:access_log /dev/null;:g" /etc/nginx/nginx.conf 264 | fi 265 | 266 | # Configure php-apc 267 | if [[ "$phpversion" == "7.3" ]]; then 268 | phpnv=/etc/php/7.3 269 | else 270 | phpnv=/etc/php/$phpversion 271 | fi 272 | sudo sh -c "echo \"cgi.fix_pathinfo = 0;\" >> $phpnv/fpm/php.ini" 273 | sudo mkdir $phpnv/conf.d >/dev/null 2>&1 274 | sudo cp etc/php5/apc.ini $phpnv/conf.d/20-apc.ini 275 | sudo chmod 644 $phpnv/conf.d/20-apc.ini 276 | sudo service nginx restart 277 | } 278 | 279 | fn_lighttpd () 280 | { 281 | sudo lighty-enable-mod fastcgi-php 282 | sudo sed -i "s/^server.document-root.*/server.document-root = \"\/var\/www$rpicamdirEsc\"/g" /etc/lighttpd/lighttpd.conf 283 | sudo sed -i "s/^server.port.*/server.port = $webport/g" /etc/lighttpd/lighttpd.conf 284 | #sudo service lighttpd restart 285 | sudo /etc/init.d/lighttpd force-reload 286 | } 287 | 288 | fn_motion () 289 | { 290 | sudo sed -i "s/^daemon.*/daemon on/g" /etc/motion/motion.conf 291 | sudo sed -i "s/^logfile.*/;logfile \/tmp\/motion.log /g" /etc/motion/motion.conf 292 | sudo sed -i "s/^; netcam_url.*/netcam_url/g" /etc/motion/motion.conf 293 | sudo sed -i "s/^netcam_url.*/netcam_url http:\/\/localhost:$webport$rpicamdirEsc\/cam_pic.php/g" /etc/motion/motion.conf 294 | if [ "$user" == "" ]; then 295 | sudo sed -i "s/^netcam_userpass.*/; netcam_userpass value/g" /etc/motion/motion.conf 296 | else 297 | sudo sed -i "s/^; netcam_userpass.*/netcam_userpass/g" /etc/motion/motion.conf 298 | sudo sed -i "s/^netcam_userpass.*/netcam_userpass $user:$webpasswd/g" /etc/motion/motion.conf 299 | fi 300 | sudo sed -i "s/^; on_event_start.*/on_event_start/g" /etc/motion/motion.conf 301 | sudo sed -i "s/^on_event_start.*/on_event_start echo -n \'1\' >\/var\/www$rpicamdirEsc\/FIFO1/g" /etc/motion/motion.conf 302 | sudo sed -i "s/^; on_event_end.*/on_event_end/g" /etc/motion/motion.conf 303 | sudo sed -i "s/^on_event_end.*/on_event_end echo -n \'0\' >\/var\/www$rpicamdirEsc\/FIFO1/g" /etc/motion/motion.conf 304 | sudo sed -i "s/control_port.*/control_port 6642/g" /etc/motion/motion.conf 305 | sudo sed -i "s/control_html_output.*/control_html_output off/g" /etc/motion/motion.conf 306 | sudo sed -i "s/^output_pictures.*/output_pictures off/g" /etc/motion/motion.conf 307 | sudo sed -i "s/^ffmpeg_output_movies on/ffmpeg_output_movies off/g" /etc/motion/motion.conf 308 | sudo sed -i "s/^ffmpeg_cap_new on/ffmpeg_cap_new off/g" /etc/motion/motion.conf 309 | sudo sed -i "s/^stream_port.*/stream_port 0/g" /etc/motion/motion.conf 310 | sudo sed -i "s/^webcam_port.*/webcam_port 0/g" /etc/motion/motion.conf 311 | sudo sed -i "s/^process_id_file/; process_id_file/g" /etc/motion/motion.conf 312 | sudo sed -i "s/^videodevice/; videodevice/g" /etc/motion/motion.conf 313 | sudo sed -i "s/^event_gap 60/event_gap 3/g" /etc/motion/motion.conf 314 | sudo chown motion:www-data /etc/motion/motion.conf 315 | sudo chmod 664 /etc/motion/motion.conf 316 | } 317 | 318 | fn_autostart () 319 | { 320 | tmpfile=$(mktemp) 321 | sudo sed '/#START/,/#END/d' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 322 | # Remove to growing plank lines. 323 | sudo awk '!NF {if (++n <= 1) print; next}; {n=0;print}' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 324 | if [ "$autostart" == "yes" ]; then 325 | if ! grep -Fq '#START RASPIMJPEG SECTION' /etc/rc.local; then 326 | sudo sed -i '/exit 0/d' /etc/rc.local 327 | sudo bash -c "cat >> /etc/rc.local" << EOF 328 | #START RASPIMJPEG SECTION 329 | mkdir -p /dev/shm/mjpeg 330 | chown www-data:www-data /dev/shm/mjpeg 331 | chmod 777 /dev/shm/mjpeg 332 | sleep 4;su -c 'raspimjpeg > /dev/null 2>&1 &' www-data 333 | if [ -e /etc/debian_version ]; then 334 | sleep 4;su -c 'php /var/www$rpicamdir/schedule.php > /dev/null 2>&1 &' www-data 335 | else 336 | sleep 4;su -s '/bin/bash' -c 'php /var/www$rpicamdir/schedule.php > /dev/null 2>&1 &' www-data 337 | fi 338 | #END RASPIMJPEG SECTION 339 | 340 | exit 0 341 | EOF 342 | else 343 | tmpfile=$(mktemp) 344 | sudo sed '/#START/,/#END/d' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 345 | # Remove to growing plank lines. 346 | sudo awk '!NF {if (++n <= 1) print; next}; {n=0;print}' /etc/rc.local > "$tmpfile" && sudo mv "$tmpfile" /etc/rc.local 347 | fi 348 | 349 | fi 350 | sudo chown root:root /etc/rc.local 351 | sudo chmod 755 /etc/rc.local 352 | } 353 | 354 | #Main install) 355 | fn_stop 356 | 357 | sudo mkdir -p /var/www$rpicamdir/media 358 | #move old material if changing from a different install folder 359 | if [ ! "$rpicamdir" == "$rpicamdirold" ]; then 360 | if [ -e /var/www$rpicamdirold/index.php ]; then 361 | sudo mv /var/www$rpicamdirold/* /var/www$rpicamdir 362 | fi 363 | fi 364 | 365 | sudo cp -r www/* /var/www$rpicamdir/ 366 | if [ -e /var/www$rpicamdir/index.html ]; then 367 | sudo rm /var/www$rpicamdir/index.html 368 | fi 369 | 370 | if [[ "$phpversion" == "7.3" ]]; then 371 | phpv=php7.3 372 | else 373 | phpv=php$phpversion 374 | fi 375 | 376 | if [ "$webserver" == "apache" ]; then 377 | sudo apt-get install -y apache2 $phpv $phpv-cli libapache2-mod-$phpv gpac motion zip gstreamer1.0-tools 378 | if [ $? -ne 0 ]; then exit; fi 379 | fn_apache 380 | elif [ "$webserver" == "nginx" ]; then 381 | sudo apt-get install -y nginx $phpv-fpm $phpv-cli $phpv-common php-apcu apache2-utils gpac motion zip gstreamer1.0-tools 382 | if [ $? -ne 0 ]; then exit; fi 383 | fn_nginx 384 | elif [ "$webserver" == "lighttpd" ]; then 385 | sudo apt-get install -y lighttpd $phpv-cli $phpv-common $phpv-cgi $phpv gpac motion zip gstreamer1.0-tools 386 | if [ $? -ne 0 ]; then exit; fi 387 | fn_lighttpd 388 | fi 389 | 390 | #Make sure user www-data has bash shell 391 | sudo sed -i "s/^www-data:x.*/www-data:x:33:33:www-data:\/var\/www:\/bin\/bash/g" /etc/passwd 392 | 393 | if [ ! -e /var/www$rpicamdir/FIFO ]; then 394 | sudo mknod /var/www$rpicamdir/FIFO p 395 | fi 396 | sudo chmod 666 /var/www$rpicamdir/FIFO 397 | 398 | if [ ! -e /var/www$rpicamdir/FIFO11 ]; then 399 | sudo mknod /var/www$rpicamdir/FIFO11 p 400 | fi 401 | sudo chmod 666 /var/www$rpicamdir/FIFO11 402 | 403 | if [ ! -e /var/www$rpicamdir/FIFO1 ]; then 404 | sudo mknod /var/www$rpicamdir/FIFO1 p 405 | fi 406 | 407 | sudo chmod 666 /var/www$rpicamdir/FIFO1 408 | 409 | if [ ! -d /dev/shm/mjpeg ]; then 410 | mkdir /dev/shm/mjpeg 411 | fi 412 | 413 | if [ "$jpglink" == "yes" ]; then 414 | if [ ! -e /var/www$rpicamdir/cam.jpg ]; then 415 | sudo ln -sf /dev/shm/mjpeg/cam.jpg /var/www$rpicamdir/cam.jpg 416 | fi 417 | fi 418 | 419 | if [ -e /var/www$rpicamdir/status_mjpeg.txt ]; then 420 | sudo rm /var/www$rpicamdir/status_mjpeg.txt 421 | fi 422 | if [ ! -e /dev/shm/mjpeg/status_mjpeg.txt ]; then 423 | echo -n 'halted' > /dev/shm/mjpeg/status_mjpeg.txt 424 | fi 425 | sudo chown www-data:www-data /dev/shm/mjpeg/status_mjpeg.txt 426 | sudo ln -sf /dev/shm/mjpeg/status_mjpeg.txt /var/www$rpicamdir/status_mjpeg.txt 427 | 428 | sudo chown -R www-data:www-data /var/www$rpicamdir 429 | sudo cp etc/sudoers.d/RPI_Cam_Web_Interface /etc/sudoers.d/ 430 | sudo chmod 440 /etc/sudoers.d/RPI_Cam_Web_Interface 431 | 432 | sudo cp -r bin/raspimjpeg /opt/vc/bin/ 433 | sudo chmod 755 /opt/vc/bin/raspimjpeg 434 | if [ ! -e /usr/bin/raspimjpeg ]; then 435 | sudo ln -s /opt/vc/bin/raspimjpeg /usr/bin/raspimjpeg 436 | fi 437 | 438 | sed -e "s/www/www$rpicamdirEsc/" etc/raspimjpeg/raspimjpeg.1 > etc/raspimjpeg/raspimjpeg 439 | if [[ `cat /proc/cmdline |awk -v RS=' ' -F= '/boardrev/ { print $2 }'` == "0x11" ]]; then 440 | sed -i 's/^camera_num 0/camera_num 1/g' etc/raspimjpeg/raspimjpeg 441 | fi 442 | if [ -e /etc/raspimjpeg ]; then 443 | $color_green; echo "Your custom raspimjpg backed up at /etc/raspimjpeg.bak"; $color_reset 444 | sudo cp -r /etc/raspimjpeg /etc/raspimjpeg.bak 445 | fi 446 | sudo cp -r etc/raspimjpeg/raspimjpeg /etc/ 447 | sudo chmod 644 /etc/raspimjpeg 448 | if [ ! -e /var/www$rpicamdir/raspimjpeg ]; then 449 | sudo ln -s /etc/raspimjpeg /var/www$rpicamdir/raspimjpeg 450 | fi 451 | 452 | sudo usermod -a -G video www-data 453 | if [ -e /var/www$rpicamdir/uconfig ]; then 454 | sudo chown www-data:www-data /var/www$rpicamdir/uconfig 455 | fi 456 | 457 | fn_motion 458 | fn_autostart 459 | 460 | if [ -e /var/www$rpicamdir/uconfig ]; then 461 | sudo chown www-data:www-data /var/www$rpicamdir/uconfig 462 | fi 463 | 464 | if [ -e /var/www$rpicamdir/schedule.php ]; then 465 | sudo rm /var/www$rpicamdir/schedule.php 466 | fi 467 | 468 | sudo sed -e "s/www/www$rpicamdirEsc/g" www/schedule.php > www/schedule.php.1 469 | sudo mv www/schedule.php.1 /var/www$rpicamdir/schedule.php 470 | sudo chown www-data:www-data /var/www$rpicamdir/schedule.php 471 | 472 | if [ $# -eq 0 ] || [ "$1" != "q" ]; then 473 | fn_reboot 474 | fi 475 | -------------------------------------------------------------------------------- /www/preview.php: -------------------------------------------------------------------------------- 1 | 0) 168 | return "style='$style'"; 169 | else 170 | return ''; 171 | } 172 | 173 | function checkMediaPath($path) { 174 | return ((realpath(dirname(MEDIA_PATH . "/$path")) == realpath(MEDIA_PATH)) && file_exists(MEDIA_PATH . "/$path")); 175 | } 176 | 177 | function getZip($files) { 178 | $zipname = 'cam_' . date("Ymd_His") . '.zip'; 179 | $cmd = 'zip -0 -q -'; // Don't compress! 180 | $size = 1000000; 181 | foreach ($files as $file) { 182 | if(checkMediaPath($file)) { 183 | $t = getFileType($file); 184 | if ($t == 't') { 185 | $lapses = findLapseFiles($file); 186 | if (!empty($lapses)) { 187 | foreach($lapses as $lapse) { 188 | $cmd .= " $lapse"; 189 | $size += filesize($lapse); 190 | } 191 | } 192 | } else if ($t == 'v' || $t == 'i') { 193 | $base = dataFilename($file); 194 | $f = MEDIA_PATH . "/$base"; 195 | if (file_exists($f)) { 196 | $cmd .= " $f"; 197 | $size += filesize($f); 198 | } 199 | $f = MEDIA_PATH . "/$base.dat"; 200 | if ($t == 'v' && file_exists($f)) { 201 | $cmd .= " $f"; 202 | $size += filesize($f); 203 | } 204 | } 205 | } 206 | } 207 | writeLog("Generating ZIP using command: $cmd ($size bytes)"); 208 | 209 | header("Content-Type: application/zip"); 210 | header("Content-Disposition: attachment; filename=\"".$zipname."\""); 211 | //header("Content-Length: " . $size); // not working (yet) 212 | 213 | $zipStream = popen($cmd, "r"); 214 | fpassthru($zipStream); 215 | pclose($zipStream); 216 | flush(); 217 | } 218 | 219 | function startVideoConvert($bFile) { 220 | global $debugString; 221 | if(checkMediaPath($bFile)) { 222 | $ft = getFileType($bFile); 223 | $fi = getFileIndex($bFile); 224 | if($ft =='t' && is_numeric($fi)) { 225 | $tFiles = findLapseFiles($bFile); 226 | $tmp = BASE_DIR . '/' . MEDIA_PATH . '/' . $ft . $fi; 227 | if (!file_exists($tmp)) { 228 | mkdir($tmp, 0777, true); 229 | } 230 | $i= 0; 231 | foreach($tFiles as $tFile) { 232 | symlink($tFile, $tmp . '/' . sprintf('i_%05d', $i) . '.jpg'); 233 | $i++; 234 | } 235 | $vFile = substr(dataFilename($bFile), 0, -3) . 'mp4'; 236 | $fp = fopen(BASE_DIR . '/' . CONVERT_CMD, 'r'); 237 | $cmd = trim(fgets($fp)); 238 | fclose($fp); 239 | $cmd = "(" . str_replace("i_%05d", "$tmp/i_%05d", $cmd) . " " . BASE_DIR . '/' . MEDIA_PATH . "/$vFile ; rm -rf $tmp;) >/dev/null 2>&1 &"; 240 | writeLog("start lapse convert:$cmd"); 241 | system($cmd); 242 | copy(MEDIA_PATH . "/$bFile", MEDIA_PATH . '/' . $vFile . '.v' . getFileIndex($bFile) .THUMBNAIL_EXT); 243 | writeLog("Convert finished"); 244 | } 245 | } 246 | } 247 | 248 | 249 | // function to deletes files and folders recursively 250 | // $deleteMainFiles true r false to delete files from the top level folder 251 | // $deleteSubFiles true or false to delete files from subfolders 252 | // Empty subfolders get removed. 253 | // $root true or false. If true (default) then top dir not removed 254 | function maintainFolders($path, $deleteMainFiles, $deleteSubFiles, $root = true) { 255 | $empty=true; 256 | foreach (glob("$path/*") as $file) { 257 | if (is_dir($file)) { 258 | if (!maintainFolders($file, $deleteMainFiles, $deleteSubFiles, false)) $empty=false; 259 | } else { 260 | if (($deleteSubFiles && !$root) || ($deleteMainFiles && $root)) { 261 | if(is_writeable($file)) unlink($file); 262 | } else { 263 | $empty=false; 264 | } 265 | } 266 | } 267 | return $empty && !$root && rmdir($path); 268 | } 269 | 270 | //function to draw 1 file on the page 271 | function drawFile($f, $ts, $sel) { 272 | $fType = getFileType($f); 273 | $rFile = dataFilename($f); 274 | $fNumber = getFileIndex($f); 275 | $lapseCount = ""; 276 | switch ($fType) { 277 | case 'v': $fIcon = 'video.png'; break; 278 | case 't': 279 | $fIcon = 'timelapse.png'; 280 | $lapseCount = '(' . count(findLapseFiles($f)). ')'; 281 | break; 282 | case 'i': $fIcon = 'image.png'; break; 283 | default : $fIcon = 'image.png'; break; 284 | } 285 | $duration =''; 286 | if (file_exists(MEDIA_PATH . "/$rFile")) { 287 | $fsz = round ((filesize_n(MEDIA_PATH . "/$rFile")) / 1024); 288 | $fModTime = filemtime(MEDIA_PATH . "/$rFile"); 289 | if ($fType == 'v') { 290 | $duration = ($fModTime - filemtime(MEDIA_PATH . "/$f")) . 's'; 291 | } 292 | } else { 293 | $fsz = 0; 294 | $fModTime = filemtime(MEDIA_PATH . "/$f"); 295 | } 296 | $fDate = @date('Y-m-d', $fModTime); 297 | $fTime = @date('H:i:s', $fModTime); 298 | $fWidth = max($ts + 4, 150); 299 | echo "
"; 300 | echo ""; 301 | if(is_writeable(MEDIA_PATH . "/$f")) { 302 | echo ""; 303 | } 304 | echo "  $fNumber "; 305 | echo ""; 306 | echo ""; 307 | echo ""; 308 | 309 | if ($fsz > 0) { 310 | if($fsz > 1024) { 311 | echo round($fsz/1024) . " MB"; 312 | } else { 313 | echo "$fsz KB"; 314 | } 315 | echo " $lapseCount $duration"; 316 | } else { 317 | echo 'Busy'; 318 | } 319 | echo "
$fDate
$fTime
"; 320 | if ($fsz > 0) echo ""; 321 | echo ""; 322 | if ($fsz > 0) echo ""; 323 | echo "
"; 324 | } 325 | 326 | function getThumbnails() { 327 | global $sortOrder; 328 | global $showTypes; 329 | global $timeFilter, $timeFilterMax; 330 | //$files = scandir(MEDIA_PATH, $sortOrder - 1); 331 | $files = scandir(MEDIA_PATH); 332 | $thumbnails = array(); 333 | $nowTime = time(); 334 | foreach($files as $file) { 335 | if($file != '.' && $file != '..' && isThumbnail($file)) { 336 | $fTime = filemtime(MEDIA_PATH . "/$file"); 337 | if ($timeFilter == 1) { 338 | $include = true; 339 | } else { 340 | $timeD = $nowTime - $fTime; 341 | if ($timeFilter == $timeFilterMax) { 342 | $include = ($timeD >= 86400 * ($timeFilter - 2)); 343 | } else { 344 | $include = ($timeD >= (86400 * ($timeFilter - 2))) && ($timeD < (($timeFilter - 1) * 86400)); 345 | } 346 | } 347 | if($include) { 348 | $fType = getFileType($file); 349 | if(($showTypes == '1') || ($showTypes == '2' && ($fType == 'i' || $fType == 't')) || ($showTypes == '3' && ($fType == 'v'))) { 350 | $thumbnails[$file] = $fType . $fTime; 351 | } 352 | } 353 | } 354 | } 355 | if ($sortOrder == 1) { 356 | asort($thumbnails); 357 | } else { 358 | arsort($thumbnails); 359 | } 360 | $thumbnails = array_keys($thumbnails); 361 | return $thumbnails; 362 | } 363 | 364 | function diskUsage() { 365 | //Get disk data 366 | echo '
'; 367 | if (file_exists("diskUsage.txt")) { 368 | $data = file_get_contents("diskUsage.txt"); 369 | $lines = explode("\n", $data); 370 | } else { 371 | $lines = array("local:local"); 372 | } 373 | $br = ""; 374 | $px = 0; 375 | foreach($lines as $line) { 376 | $fields = explode(':', $line); 377 | if(strlen($line) > 3 && count($fields) > 1) { 378 | if($fields[1] == 'local') $fields[1] = BASE_DIR . '/' . MEDIA_PATH; 379 | $totalSize = round(disk_total_space($fields[1]) / 1048576); //MB 380 | $currentAvailable = round(disk_free_space($fields[1]) / 1048576); //MB 381 | $percentUsed = round(($totalSize - $currentAvailable)/$totalSize * 100, 1); 382 | if ($percentUsed > 98) 383 | $colour = 'Red'; 384 | else if ($percentUsed > 90) 385 | $colour = 'Orange'; 386 | else 387 | $colour = 'LightGreen'; 388 | echo $br . "" . $fields[0] . ": $percentUsed% Total: $totalSize MB"; 389 | echo "
 
"; 390 | $br = '
'; 391 | $px = $px + 20; 392 | } 393 | } 394 | echo '
'; 395 | } 396 | 397 | function settingsControls() { 398 | global $previewSize,$thumbSize,$sortOrder, $showTypes; 399 | global $timeFilter, $timeFilterMax; 400 | 401 | echo TXT_PREVIEW . " "; 402 | echo "  " . TXT_THUMB . " "; 403 | echo " "; 404 | echo ' Sort '; 410 | echo ' Types '; 418 | echo ' Filter '; 430 | echo '
'; 431 | } 432 | $f = fopen(BASE_DIR . '/' . CONVERT_CMD, 'r'); 433 | $convertCmd = trim(fgets($f)); 434 | fclose($f); 435 | $thumbnails = getThumbnails(); 436 | ?> 437 | 438 | 439 | 440 | 441 | <?php echo CAM_STRING; ?> Download 442 | 443 | 444 | 445 | 446 | 447 | 448 | 454 | 455 | 456 | 457 | 464 | 465 |
466 |
467 | 482 | 483 | 489 | 490 |

> 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 |

499 | "; 503 | if(count($thumbnails) == 0) echo "

No videos/images saved

"; 504 | else { 505 | foreach($thumbnails as $file) { 506 | drawFile($file, $thumbSize, $dSelect); 507 | } 508 | } 509 | if(CONTROLS_POS == 'bottom') {echo "
";settingsControls();} 510 | ?> 511 |
512 | 513 |
514 | 515 | 516 | -------------------------------------------------------------------------------- /etc/motion/motion.conf.1: -------------------------------------------------------------------------------- 1 | # Rename this distribution example file to motion.conf 2 | # 3 | # This config file was generated by motion 3.2.12 4 | 5 | 6 | ############################################################ 7 | # Daemon 8 | ############################################################ 9 | 10 | # Start in daemon (background) mode and release terminal (default: off) 11 | daemon on 12 | 13 | # File to store the process ID, also called pid file. (default: not defined) 14 | ; process_id_file /var/run/motion/motion.pid 15 | 16 | ############################################################ 17 | # Basic Setup Mode 18 | ############################################################ 19 | 20 | # Start in Setup-Mode, daemon disabled. (default: off) 21 | setup_mode off 22 | 23 | ########################################################### 24 | # Capture device options 25 | ############################################################ 26 | 27 | # Videodevice to be used for capturing (default /dev/video0) 28 | # for FreeBSD default is /dev/bktr0 29 | ; videodevice /dev/video0 30 | 31 | # v4l2_palette allows to choose preferable palette to be use by motion 32 | # to capture from those supported by your videodevice. (default: 8) 33 | # E.g. if your videodevice supports both V4L2_PIX_FMT_SBGGR8 and 34 | # V4L2_PIX_FMT_MJPEG then motion will by default use V4L2_PIX_FMT_MJPEG. 35 | # Setting v4l2_palette to 1 forces motion to use V4L2_PIX_FMT_SBGGR8 36 | # instead. 37 | # 38 | # Values : 39 | # V4L2_PIX_FMT_SN9C10X : 0 'S910' 40 | # V4L2_PIX_FMT_SBGGR8 : 1 'BA81' 41 | # V4L2_PIX_FMT_MJPEG : 2 'MJPEG' 42 | # V4L2_PIX_FMT_JPEG : 3 'JPEG' 43 | # V4L2_PIX_FMT_RGB24 : 4 'RGB3' 44 | # V4L2_PIX_FMT_UYVY : 5 'UYVY' 45 | # V4L2_PIX_FMT_YUYV : 6 'YUYV' 46 | # V4L2_PIX_FMT_YUV422P : 7 '422P' 47 | # V4L2_PIX_FMT_YUV420 : 8 'YU12' 48 | v4l2_palette 8 49 | 50 | # Tuner device to be used for capturing using tuner as source (default /dev/tuner0) 51 | # This is ONLY used for FreeBSD. Leave it commented out for Linux 52 | ; tunerdevice /dev/tuner0 53 | 54 | # The video input to be used (default: 8) 55 | # Should normally be set to 0 or 1 for video/TV cards, and 8 for USB cameras 56 | input 8 57 | 58 | # The video norm to use (only for video capture and TV tuner cards) 59 | # Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) 60 | norm 0 61 | 62 | # The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0) 63 | frequency 0 64 | 65 | # Rotate image this number of degrees. The rotation affects all saved images as 66 | # well as mpeg movies. Valid values: 0 (default = no rotation), 90, 180 and 270. 67 | rotate 0 68 | 69 | # Image width (pixels). Valid range: Camera dependent, default: 352 70 | width 352 71 | 72 | # Image height (pixels). Valid range: Camera dependent, default: 288 73 | height 288 74 | 75 | # Maximum number of frames to be captured per second. 76 | # Valid range: 2-100. Default: 100 (almost no limit). 77 | framerate 2 78 | 79 | # Minimum time in seconds between capturing picture frames from the camera. 80 | # Default: 0 = disabled - the capture rate is given by the camera framerate. 81 | # This option is used when you want to capture images at a rate lower than 2 per second. 82 | minimum_frame_time 0 83 | 84 | # URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// or file:///) 85 | # Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined 86 | netcam_url http://localhost/cam_pic.php 87 | 88 | # Username and password for network camera (only if required). Default: not defined 89 | # Syntax is user:password 90 | ; netcam_userpass value 91 | 92 | # The setting for keep-alive of network socket, should improve performance on compatible net cameras. 93 | # 1.0: The historical implementation using HTTP/1.0, closing the socket after each http request. 94 | # keep_alive: Use HTTP/1.0 requests with keep alive header to reuse the same connection. 95 | # 1.1: Use HTTP/1.1 requests that support keep alive as default. 96 | # Default: 1.0 97 | ; netcam_http 1.0 98 | 99 | # URL to use for a netcam proxy server, if required, e.g. "http://myproxy". 100 | # If a port number other than 80 is needed, use "http://myproxy:1234". 101 | # Default: not defined 102 | ; netcam_proxy value 103 | 104 | # Set less strict jpeg checks for network cameras with a poor/buggy firmware. 105 | # Default: off 106 | netcam_tolerant_check off 107 | 108 | # Let motion regulate the brightness of a video device (default: off). 109 | # The auto_brightness feature uses the brightness option as its target value. 110 | # If brightness is zero auto_brightness will adjust to average brightness value 128. 111 | # Only recommended for cameras without auto brightness 112 | auto_brightness off 113 | 114 | # Set the initial brightness of a video device. 115 | # If auto_brightness is enabled, this value defines the average brightness level 116 | # which Motion will try and adjust to. 117 | # Valid range 0-255, default 0 = disabled 118 | brightness 0 119 | 120 | # Set the contrast of a video device. 121 | # Valid range 0-255, default 0 = disabled 122 | contrast 0 123 | 124 | # Set the saturation of a video device. 125 | # Valid range 0-255, default 0 = disabled 126 | saturation 0 127 | 128 | # Set the hue of a video device (NTSC feature). 129 | # Valid range 0-255, default 0 = disabled 130 | hue 0 131 | 132 | 133 | ############################################################ 134 | # Round Robin (multiple inputs on same video device name) 135 | ############################################################ 136 | 137 | # Number of frames to capture in each roundrobin step (default: 1) 138 | roundrobin_frames 1 139 | 140 | # Number of frames to skip before each roundrobin step (default: 1) 141 | roundrobin_skip 1 142 | 143 | # Try to filter out noise generated by roundrobin (default: off) 144 | switchfilter off 145 | 146 | 147 | ############################################################ 148 | # Motion Detection Settings: 149 | ############################################################ 150 | 151 | # Threshold for number of changed pixels in an image that 152 | # triggers motion detection (default: 1500) 153 | threshold 1500 154 | 155 | # Automatically tune the threshold down if possible (default: off) 156 | threshold_tune off 157 | 158 | # Noise threshold for the motion detection (default: 32) 159 | noise_level 32 160 | 161 | # Automatically tune the noise threshold (default: on) 162 | noise_tune on 163 | 164 | # Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined) 165 | # Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid. 166 | # (l)abeling must only be used once and the 'l' must be the last letter. 167 | # Comment out to disable 168 | despeckle EedDl 169 | 170 | # Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3 171 | # A script (on_area_detected) is started immediately when motion is 4 5 6 172 | # detected in one of the given areas, but only once during an event. 7 8 9 173 | # One or more areas can be specified with this option. (Default: not defined) 174 | ; area_detect value 175 | 176 | # PGM file to use as a sensitivity mask. 177 | # Full path name to. (Default: not defined) 178 | ; mask_file value 179 | 180 | # Dynamically create a mask file during operation (default: 0) 181 | # Adjust speed of mask changes from 0 (off) to 10 (fast) 182 | smart_mask_speed 0 183 | 184 | # Ignore sudden massive light intensity changes given as a percentage of the picture 185 | # area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled 186 | lightswitch 0 187 | 188 | # Picture frames must contain motion at least the specified number of frames 189 | # in a row before they are detected as true motion. At the default of 1, all 190 | # motion is detected. Valid range: 1 to thousands, recommended 1-5 191 | minimum_motion_frames 1 192 | 193 | # Specifies the number of pre-captured (buffered) pictures from before motion 194 | # was detected that will be output at motion detection. 195 | # Recommended range: 0 to 5 (default: 0) 196 | # Do not use large values! Large values will cause Motion to skip video frames and 197 | # cause unsmooth mpegs. To smooth mpegs use larger values of post_capture instead. 198 | pre_capture 0 199 | 200 | # Number of frames to capture after motion is no longer detected (default: 0) 201 | post_capture 0 202 | 203 | # Gap is the seconds of no motion detection that triggers the end of an event 204 | # An event is defined as a series of motion images taken within a short timeframe. 205 | # Recommended value is 60 seconds (Default). The value 0 is allowed and disables 206 | # events causing all Motion to be written to one single mpeg file and no pre_capture. 207 | gap 3 208 | 209 | # Maximum length in seconds of an mpeg movie 210 | # When value is exceeded a new mpeg file is created. (Default: 0 = infinite) 211 | max_mpeg_time 0 212 | 213 | # Always save images even if there was no motion (default: off) 214 | output_all off 215 | 216 | 217 | ############################################################ 218 | # Image File Output 219 | ############################################################ 220 | 221 | # Output 'normal' pictures when motion is detected (default: on) 222 | # Valid values: on, off, first, best, center 223 | # When set to 'first', only the first picture of an event is saved. 224 | # Picture with most motion of an event is saved when set to 'best'. 225 | # Picture with motion nearest center of picture is saved when set to 'center'. 226 | # Can be used as preview shot for the corresponding movie. 227 | output_normal off 228 | 229 | # Output pictures with only the pixels moving object (ghost images) (default: off) 230 | output_motion off 231 | 232 | # The quality (in percent) to be used by the jpeg compression (default: 75) 233 | quality 75 234 | 235 | # Output ppm images instead of jpeg (default: off) 236 | ppm off 237 | 238 | 239 | ############################################################ 240 | # FFMPEG related options 241 | # Film (mpeg) file output, and deinterlacing of the video input 242 | # The options movie_filename and timelapse_filename are also used 243 | # by the ffmpeg feature 244 | ############################################################ 245 | 246 | # Use ffmpeg to encode mpeg movies in realtime (default: off) 247 | ffmpeg_cap_new off 248 | 249 | # Use ffmpeg to make movies with only the pixels moving 250 | # object (ghost images) (default: off) 251 | ffmpeg_cap_motion off 252 | 253 | # Use ffmpeg to encode a timelapse movie 254 | # Default value 0 = off - else save frame every Nth second 255 | ffmpeg_timelapse 0 256 | 257 | # The file rollover mode of the timelapse video 258 | # Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual 259 | ffmpeg_timelapse_mode daily 260 | 261 | # Bitrate to be used by the ffmpeg encoder (default: 400000) 262 | # This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled) 263 | ffmpeg_bps 500000 264 | 265 | # Enables and defines variable bitrate for the ffmpeg encoder. 266 | # ffmpeg_bps is ignored if variable bitrate is enabled. 267 | # Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, 268 | # or the range 2 - 31 where 2 means best quality and 31 is worst. 269 | ffmpeg_variable_bitrate 0 270 | 271 | # Codec to used by ffmpeg for the video compression. 272 | # Timelapse mpegs are always made in mpeg1 format independent from this option. 273 | # Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4. 274 | # mpeg1 - gives you files with extension .mpg 275 | # mpeg4 or msmpeg4 - gives you files with extension .avi 276 | # msmpeg4 is recommended for use with Windows Media Player because 277 | # it requires no installation of codec on the Windows client. 278 | # swf - gives you a flash film with extension .swf 279 | # flv - gives you a flash video with extension .flv 280 | # ffv1 - FF video codec 1 for Lossless Encoding ( experimental ) 281 | # mov - QuickTime ( testing ) 282 | ffmpeg_video_codec swf 283 | 284 | # Use ffmpeg to deinterlace video. Necessary if you use an analog camera 285 | # and see horizontal combing on moving objects in video or pictures. 286 | # (default: off) 287 | ffmpeg_deinterlace off 288 | 289 | 290 | ############################################################ 291 | # Snapshots (Traditional Periodic Webcam File Output) 292 | ############################################################ 293 | 294 | # Make automated snapshot every N seconds (default: 0 = disabled) 295 | snapshot_interval 0 296 | 297 | 298 | ############################################################ 299 | # Text Display 300 | # %Y = year, %m = month, %d = date, 301 | # %H = hour, %M = minute, %S = second, %T = HH:MM:SS, 302 | # %v = event, %q = frame number, %t = thread (camera) number, 303 | # %D = changed pixels, %N = noise level, \n = new line, 304 | # %i and %J = width and height of motion area, 305 | # %K and %L = X and Y coordinates of motion center 306 | # %C = value defined by text_event - do not use with text_event! 307 | # You can put quotation marks around the text to allow 308 | # leading spaces 309 | ############################################################ 310 | 311 | # Locate and draw a box around the moving object. 312 | # Valid values: on, off and preview (default: off) 313 | # Set to 'preview' will only draw a box in preview_shot pictures. 314 | locate off 315 | 316 | # Draws the timestamp using same options as C function strftime(3) 317 | # Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock 318 | # Text is placed in lower right corner 319 | text_right %Y-%m-%d\n%T-%q 320 | 321 | # Draw a user defined text on the images using same options as C function strftime(3) 322 | # Default: Not defined = no text 323 | # Text is placed in lower left corner 324 | ; text_left CAMERA %t 325 | 326 | # Draw the number of changed pixed on the images (default: off) 327 | # Will normally be set to off except when you setup and adjust the motion settings 328 | # Text is placed in upper right corner 329 | text_changes off 330 | 331 | # This option defines the value of the special event conversion specifier %C 332 | # You can use any conversion specifier in this option except %C. Date and time 333 | # values are from the timestamp of the first image in the current event. 334 | # Default: %Y%m%d%H%M%S 335 | # The idea is that %C can be used filenames and text_left/right for creating 336 | # a unique identifier for each event. 337 | text_event %Y%m%d%H%M%S 338 | 339 | # Draw characters at twice normal size on images. (default: off) 340 | text_double off 341 | 342 | 343 | ############################################################ 344 | # Target Directories and filenames For Images And Films 345 | # For the options snapshot_, jpeg_, mpeg_ and timelapse_filename 346 | # you can use conversion specifiers 347 | # %Y = year, %m = month, %d = date, 348 | # %H = hour, %M = minute, %S = second, 349 | # %v = event, %q = frame number, %t = thread (camera) number, 350 | # %D = changed pixels, %N = noise level, 351 | # %i and %J = width and height of motion area, 352 | # %K and %L = X and Y coordinates of motion center 353 | # %C = value defined by text_event 354 | # Quotation marks round string are allowed. 355 | ############################################################ 356 | 357 | # Target base directory for pictures and films 358 | # Recommended to use absolute path. (Default: current working directory) 359 | target_dir /var/www/media 360 | 361 | # File path for snapshots (jpeg or ppm) relative to target_dir 362 | # Default: %v-%Y%m%d%H%M%S-snapshot 363 | # Default value is equivalent to legacy oldlayout option 364 | # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot 365 | # File extension .jpg or .ppm is automatically added so do not include this. 366 | # Note: A symbolic link called lastsnap.jpg created in the target_dir will always 367 | # point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' 368 | snapshot_filename %v-%Y%m%d%H%M%S-snapshot 369 | 370 | # File path for motion triggered images (jpeg or ppm) relative to target_dir 371 | # Default: %v-%Y%m%d%H%M%S-%q 372 | # Default value is equivalent to legacy oldlayout option 373 | # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q 374 | # File extension .jpg or .ppm is automatically added so do not include this 375 | # Set to 'preview' together with best-preview feature enables special naming 376 | # convention for preview shots. See motion guide for details 377 | jpeg_filename vthumb_%Y%m%d_%H%M%S 378 | 379 | # File path for motion triggered ffmpeg films (mpeg) relative to target_dir 380 | # Default: %v-%Y%m%d%H%M%S 381 | # Default value is equivalent to legacy oldlayout option 382 | # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S 383 | # File extension .mpg or .avi is automatically added so do not include this 384 | # This option was previously called ffmpeg_filename 385 | movie_filename %v-%Y%m%d%H%M%S 386 | 387 | # File path for timelapse mpegs relative to target_dir 388 | # Default: %Y%m%d-timelapse 389 | # Default value is near equivalent to legacy oldlayout option 390 | # For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse 391 | # File extension .mpg is automatically added so do not include this 392 | timelapse_filename %Y%m%d-timelapse 393 | 394 | 395 | ############################################################ 396 | # Live Webcam Server 397 | ############################################################ 398 | 399 | # The mini-http server listens to this port for requests (default: 0 = disabled) 400 | webcam_port 0 401 | 402 | # Quality of the jpeg (in percent) images produced (default: 50) 403 | webcam_quality 50 404 | 405 | # Output frames at 1 fps when no motion is detected and increase to the 406 | # rate given by webcam_maxrate when motion is detected (default: off) 407 | webcam_motion off 408 | 409 | # Maximum framerate for webcam streams (default: 1) 410 | webcam_maxrate 1 411 | 412 | # Restrict webcam connections to localhost only (default: on) 413 | webcam_localhost on 414 | 415 | # Limits the number of images per connection (default: 0 = unlimited) 416 | # Number can be defined by multiplying actual webcam rate by desired number of seconds 417 | # Actual webcam rate is the smallest of the numbers framerate and webcam_maxrate 418 | webcam_limit 0 419 | 420 | 421 | ############################################################ 422 | # HTTP Based Control 423 | ############################################################ 424 | 425 | # TCP/IP port for the http server to listen on (default: 0 = disabled) 426 | control_port 6642 427 | 428 | # Restrict control connections to localhost only (default: on) 429 | control_localhost on 430 | 431 | # Output for http server, select off to choose raw text plain (default: on) 432 | control_html_output off 433 | 434 | # Authentication for the http based control. Syntax username:password 435 | # Default: not defined (Disabled) 436 | ; control_authentication username:password 437 | 438 | 439 | ############################################################ 440 | # Tracking (Pan/Tilt) 441 | ############################################################ 442 | 443 | # Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo) 444 | # The generic type enables the definition of motion center and motion size to 445 | # be used with the conversion specifiers for options like on_motion_detected 446 | track_type 0 447 | 448 | # Enable auto tracking (default: off) 449 | track_auto off 450 | 451 | # Serial port of motor (default: none) 452 | ; track_port value 453 | 454 | # Motor number for x-axis (default: 0) 455 | track_motorx 0 456 | 457 | # Motor number for y-axis (default: 0) 458 | track_motory 0 459 | 460 | # Maximum value on x-axis (default: 0) 461 | track_maxx 0 462 | 463 | # Maximum value on y-axis (default: 0) 464 | track_maxy 0 465 | 466 | # ID of an iomojo camera if used (default: 0) 467 | track_iomojo_id 0 468 | 469 | # Angle in degrees the camera moves per step on the X-axis 470 | # with auto-track (default: 10) 471 | # Currently only used with pwc type cameras 472 | track_step_angle_x 10 473 | 474 | # Angle in degrees the camera moves per step on the Y-axis 475 | # with auto-track (default: 10) 476 | # Currently only used with pwc type cameras 477 | track_step_angle_y 10 478 | 479 | # Delay to wait for after tracking movement as number 480 | # of picture frames (default: 10) 481 | track_move_wait 10 482 | 483 | # Speed to set the motor to (stepper motor option) (default: 255) 484 | track_speed 255 485 | 486 | # Number of steps to make (stepper motor option) (default: 40) 487 | track_stepsize 40 488 | 489 | 490 | ############################################################ 491 | # External Commands, Warnings and Logging: 492 | # You can use conversion specifiers for the on_xxxx commands 493 | # %Y = year, %m = month, %d = date, 494 | # %H = hour, %M = minute, %S = second, 495 | # %v = event, %q = frame number, %t = thread (camera) number, 496 | # %D = changed pixels, %N = noise level, 497 | # %i and %J = width and height of motion area, 498 | # %K and %L = X and Y coordinates of motion center 499 | # %C = value defined by text_event 500 | # %f = filename with full path 501 | # %n = number indicating filetype 502 | # Both %f and %n are only defined for on_picture_save, 503 | # on_movie_start and on_movie_end 504 | # Quotation marks round string are allowed. 505 | ############################################################ 506 | 507 | # Do not sound beeps when detecting motion (default: on) 508 | # Note: Motion never beeps when running in daemon mode. 509 | quiet on 510 | 511 | # Command to be executed when an event starts. (default: none) 512 | # An event starts at first motion detected after a period of no motion defined by gap 513 | on_event_start echo -n '1' > /var/www/FIFO1 514 | 515 | # Command to be executed when an event ends after a period of no motion 516 | # (default: none). The period of no motion is defined by option gap. 517 | on_event_end echo -n '0' > /var/www/FIFO1 518 | 519 | # Command to be executed when a picture (.ppm|.jpg) is saved (default: none) 520 | # To give the filename as an argument to a command append it with %f 521 | ; on_picture_save value 522 | 523 | # Command to be executed when a motion frame is detected (default: none) 524 | ; on_motion_detected value 525 | 526 | # Command to be executed when motion in a predefined area is detected 527 | # Check option 'area_detect'. (default: none) 528 | ; on_area_detected value 529 | 530 | # Command to be executed when a movie file (.mpg|.avi) is created. (default: none) 531 | # To give the filename as an argument to a command append it with %f 532 | ; on_movie_start value 533 | 534 | # Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) 535 | # To give the filename as an argument to a command append it with %f 536 | ; on_movie_end value 537 | 538 | # Command to be executed when a camera can't be opened or if it is lost 539 | # NOTE: There is situations when motion doesn't detect a lost camera! 540 | # It depends on the driver, some drivers don't detect a lost camera at all 541 | # Some hang the motion thread. Some even hang the PC! (default: none) 542 | ; on_camera_lost value 543 | 544 | ############################################################ 545 | # Common Options For MySQL and PostgreSQL database features. 546 | # Options require the MySQL/PostgreSQL options to be active also. 547 | ############################################################ 548 | 549 | # Log to the database when creating motion triggered image file (default: on) 550 | sql_log_image on 551 | 552 | # Log to the database when creating a snapshot image file (default: on) 553 | sql_log_snapshot on 554 | 555 | # Log to the database when creating motion triggered mpeg file (default: off) 556 | sql_log_mpeg off 557 | 558 | # Log to the database when creating timelapse mpeg file (default: off) 559 | sql_log_timelapse off 560 | 561 | # SQL query string that is sent to the database 562 | # Use same conversion specifiers has for text features 563 | # Additional special conversion specifiers are 564 | # %n = the number representing the file_type 565 | # %f = filename with full path 566 | # Default value: 567 | # insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') 568 | sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') 569 | 570 | 571 | ############################################################ 572 | # Database Options For MySQL 573 | ############################################################ 574 | 575 | # Mysql database to log to (default: not defined) 576 | ; mysql_db value 577 | 578 | # The host on which the database is located (default: localhost) 579 | ; mysql_host value 580 | 581 | # User account name for MySQL database (default: not defined) 582 | ; mysql_user value 583 | 584 | # User password for MySQL database (default: not defined) 585 | ; mysql_password value 586 | 587 | 588 | ############################################################ 589 | # Database Options For PostgreSQL 590 | ############################################################ 591 | 592 | # PostgreSQL database to log to (default: not defined) 593 | ; pgsql_db value 594 | 595 | # The host on which the database is located (default: localhost) 596 | ; pgsql_host value 597 | 598 | # User account name for PostgreSQL database (default: not defined) 599 | ; pgsql_user value 600 | 601 | # User password for PostgreSQL database (default: not defined) 602 | ; pgsql_password value 603 | 604 | # Port on which the PostgreSQL database is located (default: 5432) 605 | ; pgsql_port 5432 606 | 607 | 608 | ############################################################ 609 | # Video Loopback Device (vloopback project) 610 | ############################################################ 611 | 612 | # Output images to a video4linux loopback device 613 | # The value '-' means next available (default: not defined) 614 | ; video_pipe value 615 | 616 | # Output motion images to a video4linux loopback device 617 | # The value '-' means next available (default: not defined) 618 | ; motion_video_pipe value 619 | 620 | 621 | ############################################################## 622 | # Thread config files - One for each camera. 623 | # Except if only one camera - You only need this config file. 624 | # If you have more than one camera you MUST define one thread 625 | # config file for each camera in addition to this config file. 626 | ############################################################## 627 | 628 | # Remember: If you have more than one camera you must have one 629 | # thread file for each camera. E.g. 2 cameras requires 3 files: 630 | # This motion.conf file AND thread1.conf and thread2.conf. 631 | # Only put the options that are unique to each camera in the 632 | # thread config files. 633 | ; thread /usr/local/etc/thread1.conf 634 | ; thread /usr/local/etc/thread2.conf 635 | ; thread /usr/local/etc/thread3.conf 636 | ; thread /usr/local/etc/thread4.conf 637 | 638 | -------------------------------------------------------------------------------- /www/index.php: -------------------------------------------------------------------------------- 1 | 2 | 'average', 'Spot' => 'spot', 'Backlit' => 'backlit', 'Matrix' => 'matrix'); 9 | $options_em = array('Off' => 'off', 'Auto' => 'auto', 'Night' => 'night', 'Nightpreview' => 'nightpreview', 'Backlight' => 'backlight', 'Spotlight' => 'spotlight', 'Sports' => 'sports', 'Snow' => 'snow', 'Beach' => 'beach', 'Verylong' => 'verylong', 'Fixedfps' => 'fixedfps'); 10 | $options_wb = array('Off' => 'off', 'Auto' => 'auto', 'Sun' => 'sun', 'Cloudy' => 'cloudy', 'Shade' => 'shade', 'Tungsten' => 'tungsten', 'Fluorescent' => 'fluorescent', 'Incandescent' => 'incandescent', 'Flash' => 'flash', 'Horizon' => 'horizon', 'Greyworld' => 'greyworld'); 11 | // Remove some options (Colourpoint and colourbalance kill the camera) 12 | //$options_ie = array('None' => 'none', 'Negative' => 'negative', 'Solarise' => 'solarise', 'Sketch' => 'sketch', 'Denoise' => 'denoise', 'Emboss' => 'emboss', 'Oilpaint' => 'oilpaint', 'Hatch' => 'hatch', 'Gpen' => 'gpen', 'Pastel' => 'pastel', 'Watercolour' => 'watercolour', 'Film' => 'film', 'Blur' => 'blur', 'Saturation' => 'saturation', 'Colourswap' => 'colourswap', 'Washedout' => 'washedout', 'Posterise' => 'posterise', 'Colourpoint' => 'colourpoint', 'ColourBalance' => 'colourbalance', 'Cartoon' => 'cartoon', 'DeInterlaceDouble' => 'deinterlacedouble', 'DeInterlaceAdv' => 'deinterlaceadv', 'DeInterlaceFast' => 'deinterlacefast'); 13 | $options_ie = array('None' => 'none', 'Negative' => 'negative', 'Solarise' => 'solarise', 'Sketch' => 'sketch', 'Denoise' => 'denoise', 'Emboss' => 'emboss', 'Oilpaint' => 'oilpaint', 'Hatch' => 'hatch', 'Gpen' => 'gpen', 'Pastel' => 'pastel', 'Watercolour' => 'watercolour', 'Film' => 'film', 'Blur' => 'blur', 'Saturation' => 'saturation', 'Colourswap' => 'colourswap', 'Washedout' => 'washedout', 'Posterise' => 'posterise', 'Cartoon' => 'cartoon'); 14 | $options_ce_en = array('Disabled' => '0', 'Enabled' => '1'); 15 | $options_ro = array('No rotate' => '0', 'Rotate_90' => '90', 'Rotate_180' => '180', 'Rotate_270' => '270'); 16 | $options_fl = array('None' => '0', 'Horizontal' => '1', 'Vertical' => '2', 'Both' => '3'); 17 | $options_bo = array('Off' => '0', 'Background' => '2'); 18 | $options_av = array('V2' => '2', 'V3' => '3'); 19 | $options_at_en = array('Disabled' => '0', 'Enabled' => '1'); 20 | $options_ac_en = array('Disabled' => '0', 'Enabled' => '1'); 21 | $options_ab = array('Off' => '0', 'On' => '1'); 22 | $options_vs = array('Off' => '0', 'On' => '1'); 23 | $options_rl = array('Off' => '0', 'On' => '1'); 24 | $options_vp = array('Off' => '0', 'On' => '1'); 25 | $options_mx = array('Internal' => '0', 'External' => '1', 'Monitor' => '2'); 26 | $options_mf = array('Off' => '0', 'On' => '1'); 27 | $options_cn = array('First' => '1', 'Second' => '2'); 28 | $options_st = array('Off' => '0', 'On' => '1'); 29 | $options_hp = array('Off' => '0', 'On' => '1'); 30 | 31 | function initCamPos() { 32 | $tr = fopen("pipan_bak.txt", "r"); 33 | if($tr){ 34 | while(($line = fgets($tr)) != false) { 35 | $vals = explode(" ", $line); 36 | echo ''; 37 | } 38 | fclose($tr); 39 | } 40 | } 41 | 42 | function user_buttons() { 43 | $buttonString = ""; 44 | $buttonCount = 0; 45 | if (file_exists("userbuttons")) { 46 | $lines = array(); 47 | $data = file_get_contents("userbuttons"); 48 | $lines = explode("\n", $data); 49 | foreach($lines as $line) { 50 | if (strlen($line) && (substr($line, 0, 1) != '#') && $buttonCount < 12) { 51 | $index = explode(",",$line); 52 | if ($index !== false) { 53 | $buttonName = $index[0]; 54 | $macroName = $index[1]; 55 | $className = $index[2]; 56 | if ($className == false) { 57 | $className = "btn btn-primary"; 58 | } 59 | if (count($index) > 3) { 60 | $otherAtt = $index[3]; 61 | } else { 62 | $otherAtt = ""; 63 | } 64 | $buttonString .= '' . "\r\n"; 65 | $buttonCount += 1; 66 | } 67 | } 68 | } 69 | } 70 | if (strlen($buttonString)) { 71 | echo '
' . $buttonString . "
\r\n"; 72 | } 73 | } 74 | 75 | function pan_controls() { 76 | $mode = 0; 77 | if (file_exists("pipan_on")){ 78 | initCamPos(); 79 | $mode = 1; 80 | } else if (file_exists("servo_on")){ 81 | $mode = 2; 82 | } 83 | if ($mode <> 0) { 84 | echo ''; 85 | echo "
"; 86 | echo "
"; 87 | echo "
"; 88 | echo "
"; 89 | echo "
"; 90 | echo "
"; 91 | echo "
"; 92 | echo "
"; 93 | } 94 | } 95 | 96 | function pilight_controls() { 97 | echo ""; 98 | echo "Pi-Light:"; 99 | echo ""; 100 | echo "R: "; 101 | echo "G: "; 102 | echo "B:
"; 103 | echo ""; 104 | echo ""; 105 | echo ""; 106 | } 107 | 108 | function getExtraStyles() { 109 | $files = scandir('css'); 110 | foreach($files as $file) { 111 | if(substr($file,0,3) == 'es_') { 112 | echo "'; 113 | } 114 | } 115 | } 116 | 117 | 118 | function makeOptions($options, $selKey) { 119 | global $config; 120 | switch ($selKey) { 121 | case 'flip': 122 | $cvalue = (($config['vflip'] == 'true') || ($config['vflip'] == 1) ? 2:0); 123 | $cvalue += (($config['hflip'] == 'true') || ($config['hflip'] == 1) ? 1:0); 124 | break; 125 | case 'MP4Box': 126 | $cvalue = $config[$selKey]; 127 | if ($cvalue == 'background') $cvalue = 2; 128 | break; 129 | default: $cvalue = $config[$selKey]; break; 130 | } 131 | if ($cvalue == 'false') $cvalue = 0; 132 | else if ($cvalue == 'true') $cvalue = 1; 133 | foreach($options as $name => $value) { 134 | if ($cvalue != $value) { 135 | $selected = ''; 136 | } else { 137 | $selected = ' selected'; 138 | } 139 | echo ""; 140 | } 141 | } 142 | 143 | function makeInput($id, $size, $selKey='', $type='text') { 144 | global $config, $debugString; 145 | if ($selKey == '') $selKey = $id; 146 | switch ($selKey) { 147 | case 'tl_interval': 148 | if (array_key_exists($selKey, $config)) { 149 | $value = $config[$selKey] / 10; 150 | } else { 151 | $value = 3; 152 | } 153 | break; 154 | case 'watchdog_interval': 155 | if (array_key_exists($selKey, $config)) { 156 | $value = $config[$selKey] / 10; 157 | } else { 158 | $value = 0; 159 | } 160 | break; 161 | default: $value = $config[$selKey]; break; 162 | } 163 | echo ""; 164 | } 165 | 166 | function macroUpdates() { 167 | global $config, $debugString, $macros; 168 | $m = 0; 169 | $mTable = ''; 170 | foreach($macros as $macro) { 171 | $value = $config[$macro]; 172 | if(substr($value,0,1) == '-') { 173 | $checked = ''; 174 | $value = substr($value,1); 175 | } else { 176 | $checked = 'checked'; 177 | } 178 | $mTable .= "Macro:$macro\r\n"; 179 | $mTable .= "\r\n"; 180 | $mTable .= "'; 181 | $m++; 182 | } 183 | echo $mTable; 184 | } 185 | 186 | function getImgWidth() { 187 | global $config; 188 | if($config['vector_preview']) 189 | return 'style="width:' . $config['width'] . 'px;"'; 190 | else 191 | return ''; 192 | } 193 | 194 | function getLoadClass() { 195 | global $config; 196 | if(array_key_exists('fullscreen', $config) && $config['fullscreen'] == 1) 197 | return 'class="fullscreen" '; 198 | else 199 | return ''; 200 | } 201 | 202 | function simple_button() { 203 | global $toggleButton, $userLevel; 204 | if ($toggleButton != "Off" && $userLevel > USERLEVEL_MIN) { 205 | echo ''; 206 | } 207 | } 208 | 209 | if (isset($_POST['extrastyle'])) { 210 | $extra = $_POST['extrastyle']; 211 | if ((strpos($extra, '/') === false) && file_exists('css/' . $extra)) { 212 | $fp = fopen(BASE_DIR . '/css/extrastyle.txt', "w"); 213 | fwrite($fp, $extra); 214 | fclose($fp); 215 | } 216 | } 217 | 218 | function getDisplayStyle($context, $userLevel) { 219 | global $Simple; 220 | if ($Simple == 1) { 221 | echo 'style="display:none;"'; 222 | } else { 223 | switch($context) { 224 | case 'navbar': 225 | if ((int)$userLevel < (int)USERLEVEL_MEDIUM) 226 | echo 'style="display:none;"'; 227 | break; 228 | case 'preview': 229 | if ((int)$userLevel < (int)USERLEVEL_MINP) 230 | echo 'style="display:none;"'; 231 | break; 232 | case 'actions': 233 | if ((int)$userLevel < (int)USERLEVEL_MEDIUM) 234 | echo 'style="display:none;"'; 235 | break; 236 | case 'settings': 237 | if ((int)$userLevel != (int)USERLEVEL_MAX) 238 | echo 'style="display:none;"'; 239 | break; 240 | } 241 | } 242 | } 243 | 244 | $toggleButton = "Off"; 245 | $Simple = 0; 246 | $allowSimple = "SimpleOn"; 247 | if(isset($_COOKIE["display_mode"])) { 248 | if($_COOKIE["display_mode"] == "Full") { 249 | $allowSimple = "SimpleOff"; 250 | $toggleButton = "Simple"; 251 | $Simple = 2; 252 | } else if($_COOKIE["display_mode"] == "Simple") { 253 | $allowSimple = "SimpleOff"; 254 | $toggleButton = "Full"; 255 | $Simple = 1; 256 | } else { 257 | $allowSimple = "SimpleOn"; 258 | $toggleButton = "Off"; 259 | $Simple = 0; 260 | } 261 | } 262 | 263 | $streamButton = "MJPEG-Stream"; 264 | $mjpegmode = 0; 265 | if(isset($_COOKIE["stream_mode"])) { 266 | if($_COOKIE["stream_mode"] == "MJPEG-Stream") { 267 | $streamButton = "Default-Stream"; 268 | $mjpegmode = 1; 269 | } 270 | } 271 | $config = readConfig($config, CONFIG_FILE1); 272 | $config = readConfig($config, CONFIG_FILE2); 273 | $video_fps = $config['video_fps']; 274 | $divider = $config['divider']; 275 | $user = getUser(); 276 | writeLog("Logged in user:" . $user . ":"); 277 | $userLevel = getUserLevel($user); 278 | writeLog("UserLevel " . $userLevel); 279 | ?> 280 | 281 | 282 | 283 | 284 | <?php echo CAM_STRING; ?> 285 | 286 | 287 | 288 | 289 | 290 | 291 | );', 100);"> 292 | 299 | 300 |
301 |
302 | onclick="toggle_fullscreen(this);" src="./loading.jpg">
303 |
304 | > 305 | > 306 | > 307 | > 308 | > 309 |
310 |
311 |
312 | 313 | 314 | >Download Videos and Images 315 |    316 | >Edit motion settings   317 | >Edit schedule settings 318 |
319 | 320 |
321 |
> 322 |
323 |
324 |

325 | Camera Settings 326 |

327 |
328 |
329 |
330 | 331 | 332 | 333 | 350 | 351 | 0): ?> 352 | 353 | 354 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 373 | 374 | 375 | 376 | 379 | 380 | 381 | 382 | 386 | 387 | 388 | 389 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 476 | 477 | 478 | 479 | 481 | 482 | 483 | 484 | 487 | 488 | 489 | 490 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 506 | 507 | 508 | 509 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 535 | 536 | 537 | 538 | 539 | 540 |
Resolutions:Load Preset:
343 | Custom Values:
344 | Video res: xpx
345 | Video fps: recording, boxing
346 | FPS divider:
347 | Image res: xpx
348 | 349 |
Camera select (Compute module only) 355 | Use camera: 356 |
Timelapse-Interval (0.1...3200):s
Video Split (seconds, default 0=off):s
Annotation (max 127 characters): 370 | Text:
371 | Background: 372 |
Annotation size(0-99): 377 | 378 |
Custom text color: 383 | y:u:v = :: 384 | 385 |
Custom background color: 390 | y:u:v = :: 391 | 392 |
Buffer (1000... ms), default 0:
Sharpness (-100...100), default 0:
Contrast (-100...100), default 0: 406 |
Brightness (0...100), default 50:
Saturation (-100...100), default 0:
ISO (100...800), default 0:
Metering Mode, default 'average':
Video Stabilisation, default: 'off'
Exposure Compensation (-10...10), default 0:
Exposure Mode, default 'auto':
White Balance, default 'auto':
White Balance Gains (x100): gain_r gain_b 443 | 444 |
Image Effect, default 'none':
Colour Effect, default 'disabled': 453 | u:v = : 454 | 455 |
Image Statistics, default 'Off':
Rotation, default 0:
Flip, default 'none':
Sensor Region, default 0/0/65536/65536: 472 | x: y:
473 | w: h: 474 | 475 |
Shutter speed (0...CameraMax uS), default 0: 480 |
Image quality (0...100), default 10: 485 | 486 |
Preview quality (1...100), default 10:
Width (128...1024), default 512:
Divider (1-16), default 1:
491 | Quality:
492 | Width:
493 | Divider:
494 | 495 |
Raw Layer, default: 'off'
Video bitrate (0...25000000), default 17000000: 504 | 505 |
Minimise frag (0/1), default 0:
Init Quantisation, default 25:
Encoding qp, default 31:
510 | MF:
511 | IQ:
512 | QP:
513 | 514 |
MP4 Boxing mode :
Watchdog, default interval 3s, errors 3s:Interval s    Errors 523 | 524 |
Motion detect mode:
Log size lines, default 5000: 533 | 534 |
HDMI Preview, default: 'off'
541 |
542 |
543 |
544 |
> 545 |
546 |

547 | Motion Settings 548 |

549 |
550 |
551 |
552 | 553 | 554 | 555 | 558 | 559 | 560 | 561 | 564 | 565 | 566 | 567 | 570 | 571 | 572 | 573 | 576 | 577 | 578 | 579 | 582 | 583 | 584 | 585 | 588 | 589 | 590 | 591 | 594 | 595 | 596 | 597 | 600 | 601 | 602 | 603 | 604 | 605 |
Motion Vector Preview: 556 | 557 |
Noise level (1-255 / >1000): 562 | 563 |
Threshold (1-32000): 568 | 569 |
Clipping factor (2-50), default 3: 574 | 575 |
Mask Image: 580 | 581 |
Delay Frames to detect: 586 | 587 |
Change Frames to start: 592 | 593 |
Still Frames to stop: 598 | 599 |
Save vectors to .dat:
(Uses more space)
606 |
607 |
608 |
609 |
610 |
611 |

612 | System 613 |

614 |
615 |
616 |
617 | 618 | 619 | 620 | 621 | 622 |
623 |
Style 624 | 627 |   628 |
629 | Set Date/Time 630 | 631 | 632 |
633 |
634 |
635 |
636 | 650 |
651 |
652 | "; ?> 653 | 654 | 655 | --------------------------------------------------------------------------------