Added ENV variables for all config, Fixed error in cleaner script

Update cleaner.py, docker-compose.yml, and 2 more files...
This commit is contained in:
Herwin Bozet (NibblePoker) 2023-05-15 18:50:30 +02:00
parent 6b3ef62c4b
commit d7538b950e
4 changed files with 106 additions and 40 deletions

View File

@ -3,8 +3,14 @@ import signal
import sys import sys
import time import time
# Keep files for 73 hours (3 days + 1 hours) # Keep files for 72 hours (3 days)
MAX_FILE_AGE_SECONDS = (72 + 1) * 60 * 60 try:
MAX_FILE_AGE_SECONDS = int(os.environ.get('NP_MAX_FILE_AGE_HOURS', "72")) * 60 * 60
except err:
print("Error: ")
print(err)
print("Using 72 hours instead !")
MAX_FILE_AGE_SECONDS = 72 * 60 * 60
# Once done cleaning, sleep for 5 minutes # Once done cleaning, sleep for 5 minutes
SLEEP_TIME_SECONDS = 5 * 60 SLEEP_TIME_SECONDS = 5 * 60
@ -37,7 +43,7 @@ start_time = time.time()
file_deleted_count = 0 file_deleted_count = 0
for item in os.listdir("/data/"): for item in os.listdir("/data/"):
item_path = os.path.join(folder_path, item) item_path = os.path.join("/data/", item)
if os.path.isdir(item_path): if os.path.isdir(item_path):
file_deleted_count = file_deleted_count + delete_old_files(item_path, start_time) file_deleted_count = file_deleted_count + delete_old_files(item_path, start_time)

View File

@ -32,6 +32,7 @@ services:
dockerfile: Dockerfile_cleaner dockerfile: Dockerfile_cleaner
environment: environment:
- TZ=Europe/Brussels - TZ=Europe/Brussels
- "NP_MAX_FILE_AGE_HOURS=72"
volumes: volumes:
- ./recordings:/data - ./recordings:/data
- ./cleaner.py:/app/app.py:ro - ./cleaner.py:/app/app.py:ro
@ -43,6 +44,10 @@ services:
ports: ports:
- 26880:80 - 26880:80
environment: environment:
- "NP_CAM_cam1=Camera #1"
- "NP_CAM_cam2=Camera #2"
- "NP_TITLE=NibblePoker's Mini CCTV NVR"
- "NP_FOOTER=Made by <i>BOZET Herwin</i>"
- TZ=Europe/Brussels - TZ=Europe/Brussels
volumes: volumes:
- ./htdocs:/var/www/html # Cannot be "ro" since the recordings are mounted into it. - ./htdocs:/var/www/html # Cannot be "ro" since the recordings are mounted into it.

View File

@ -1,10 +1,17 @@
<?php <?php
// List of all available cameras // Grabbing the camera's info
$camsInfo = [
# Format: [camId, camName] $camsInfo = []; // Format: [[camId, camName], ...]
["cam1", "Cam #1"],
["cam2", "Cam #2"] foreach ($_ENV as $envKey => $envValue) {
]; if (strpos($envKey, 'NP_CAM_') === 0) {
array_push($camsInfo, [substr($envKey, strlen('NP_CAM_')), $envValue]);
}
}
// Grabbing the other env variables.
$pageTitle = $_ENV['NP_TITLE'] ?? 'NibblePoker\'s Mini CCTV NVR';
$pageFooter = $_ENV['NP_FOOTER'] ?? 'Made by <a href="https://github.com/aziascreations">BOZET Herwin</a> on <a href="https://github.com/aziascreations/Docker-Mini-CCTV-NVR">Github</a>';
// Root location of all recordings. (Not used yet) // Root location of all recordings. (Not used yet)
$rootLocation = "./data/"; $rootLocation = "./data/";
@ -102,7 +109,7 @@ function sizeFormat($bytes) {
<meta name="viewport" <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>NibblePoker's Mini CCTV NVR</title> <title><?php echo($pageTitle); ?></title>
<link rel="stylesheet" href="/css/simplette.all.min.css"> <link rel="stylesheet" href="/css/simplette.all.min.css">
<style> <style>
#video-selector { #video-selector {
@ -139,13 +146,13 @@ function sizeFormat($bytes) {
?> ?>
</ul> </ul>
</nav> </nav>
<header><h1><b>NibblePoker's Mini CCTV NVR</b></h1></header> <header><h1><b><?php echo($pageTitle); ?></b></h1></header>
<hr><hr> <hr><hr>
<div class="margin-container auto-paragraph-margin"> <div class="margin-container auto-paragraph-margin">
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
<td> <td>
<h3 style="width: 100%;">Caméra: <i><?php echo($camName); ?></i></h3> <h3 style="width: 100%;">Camera: <i><?php echo($camName); ?></i></h3>
</td> </td>
<td> <td>
<span style="float: right;"><?php <span style="float: right;"><?php
@ -201,8 +208,7 @@ function sizeFormat($bytes) {
</div> </div>
<hr><hr> <hr><hr>
<footer> <footer>
<!-- Feel free to change this to something less invasive, or simply stats. Your imagination is the limit :) --> <p><?php echo($pageFooter); ?></p>
<p>Made by <a href="https://github.com/aziascreations">BOZET Herwin</a> on <a href="https://github.com/aziascreations/Docker-Mini-CCTV-NVR">Github</a></p>
</footer> </footer>
<script> <script>
<?php <?php

101
readme.md
View File

@ -2,16 +2,19 @@
A mini docker stack that allows you to easily record, clean and serve CCTV recordings made over RSTP while using a A mini docker stack that allows you to easily record, clean and serve CCTV recordings made over RSTP while using a
minimal amount of system resources. minimal amount of system resources.
## Preamble This stack is mainly intended to be used as a backup when other and more complete solutions crash or need to be shutdown.
This stack records the camera's streams as-is and doens't re-encode or compress it which can cause it to use more disk space.<br>
The highest I've got on my side is around 70 GiB per day for a 4K-ish cam with AAC audio.
It is highly recommended to put the web page behind a secure reverse-proxy that requires authentication for aditional security. ## Preamble
This stack records the camera's streams as-is and doens't re-encode or compress it which uses more disk space.<br>
See "[Usage statistics example](#usage-statistics-example)" for an example.
If served out of your LAN, the web server should be behind a secure reverse-proxy that requires authentication.
## Setup ## Setup
Since the stack is a bit rough around the edges for simplicity''s sake you **will** need to setup a couple of things beforehand. All of the setup is done through environment variables in the [docker-compose.yml](docker-compose.yml) file.
It should only take 2-3 minutes if you already have the RTSP URL on hand however. It should only take 2-3 minutes if you already have the RTSP URL on hand.<br>
If you don't have them, you should see your camera's user manual and test the URLs with [VLC](https://www.videolan.org/vlc/).
### Cameras ### Cameras
Each recording container needs to be given a RSTP stream URL and a unique folder into which the recordings will go. Each recording container needs to be given a RSTP stream URL and a unique folder into which the recordings will go.
@ -19,7 +22,7 @@ Each recording container needs to be given a RSTP stream URL and a unique folder
The URL must be given via the `NP_CCTV_URL` environment variable, and the output folder via a mounted volume that is The URL must be given via the `NP_CCTV_URL` environment variable, and the output folder via a mounted volume that is
mounted as `/data` in the container. mounted as `/data` in the container.
Here is a simple example: #### Example:
```yaml ```yaml
cctv_recorder_cam1: cctv_recorder_cam1:
container_name: cctv-recorder-cam1 container_name: cctv-recorder-cam1
@ -37,26 +40,61 @@ Here is a simple example:
This example will use the `rtsp://user:password@address:554/sub-path` URL and will put its recordings in `./recordings/cam1`. This example will use the `rtsp://user:password@address:554/sub-path` URL and will put its recordings in `./recordings/cam1`.
### Cleaner ### Cleaner
The cleaner script named [cleaner.py](cleaner.py) only requires you to change 1 variable located near the top of the file. The cleaner script named [cleaner.py](cleaner.py) only requires you to set 1 environment variable named `NP_MAX_FILE_AGE_HOURS`
to the max amount of hours any recording should be kept.
The variable named `MAX_FILE_AGE_SECONDS` is used to indicate how long recordings should be kept for and is set to If not set, the script will simply clean any recordings older than 72 hours.
73 hours by default.
It can also be changed as you wish without recreating the stack since the script is "reloaded" on each run. #### Example
```yaml
### Web interface cctv_cleaner:
The web interface only requires you to give it the list of all cams' IDs and a friendly name in the `$camsInfo` variable. container_name: cctv-cleaner
build:
This variable is located at the top of the [htdocs/index.php](htdocs/index.php) file and should look like this: context: .
```php dockerfile: Dockerfile_cleaner
$camsInfo = [ environment:
# Format: [camId, camName] - TZ=Europe/Brussels
["cam1", "Cam #1"], - "NP_MAX_FILE_AGE_HOURS=72"
["cam2", "Cam #2"] volumes:
]; - ./recordings:/data
- ./cleaner.py:/app/app.py:ro
restart: unless-stopped
``` ```
The cam's ID refers to a subfolder of `./recordings` into which this cam's recordings are found as `.mp4` or `.mkv` files. ### Web interface
The web interface provides more customization options, but at its core, it only requires the camera's environment variables to be set.
Each camera requires one of the following environment variable:<br>
`NP_CAM_<camId> = <Camera's name>`
Here is an example for `cam1` if named as `Camera #1`:<br>
`NP_CAM_cam1 = Camera #1`
#### Other variables
| Variable | Description |
| ----------- | -------------------------- |
| `NP_TITLE` | Page's title |
| `NP_FOOTER` | Page's footer HTML content |
#### Example:
```yaml
cctv_web:
container_name: cctv-web
image: php:apache
ports:
- 26880:80
environment:
- TZ=Europe/Brussels
- "NP_CAM_cam1=Camera #1"
- "NP_CAM_cam2=Camera #2"
- "NP_TITLE=NibblePoker's Mini CCTV NVR"
- "NP_FOOTER=Made by <i>BOZET Herwin</i>"
volumes:
- ./htdocs:/var/www/html # Cannot be ":ro" since the recordings are mounted into it.
- ./apache2.conf:/etc/apache2/apache2.conf:ro
- ./recordings:/var/www/html/data:ro
restart: unless-stopped
```
## Startup ## Startup
Once you have finished setting up the stack, you can simply run the following command: Once you have finished setting up the stack, you can simply run the following command:
@ -65,12 +103,23 @@ docker-compose up --build -d
``` ```
## Screenshots ## Screenshots
### Home page
![alt text](screenshots/home.png) ![alt text](screenshots/home.png)
### Camera's page with blurred preview
![alt text](screenshots/cam.png) ![alt text](screenshots/cam.png)
## Usage statistics example
* NanoPi R4S 4GB
* Uses 0.008 kWh / 8 Watts with other containers and USB HDD & USB SSD
* 4 IP Cameras
* All H.256 4k RTSP TCP streams
* Around 220 GB of data per day
* Around 20.4 Mbit/s or 2.6 MB/s
* Less than 200MB of RAM usage
* ~32 MB per recorder
* 4 MB for cleaner
* 4 MB for web server
* Uses ~10% of CPU on average over 6 cores
* Average of 15% per recorder
* Average of 1-5% on cleaner and web server
## License ## License
This software, as well as the [Simplette CSS Stylesheet](https://github.com/aziascreations/Simplette-CSS-Stylesheet) This software, as well as the [Simplette CSS Stylesheet](https://github.com/aziascreations/Simplette-CSS-Stylesheet)