first commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
venv
|
||||
__pycache__
|
||||
timelapse
|
||||
static/output
|
||||
archive
|
||||
output
|
||||
app/config/cameras.yaml
|
||||
@@ -0,0 +1,75 @@
|
||||
# CameraCrawler
|
||||
|
||||
This is a flask website to display Images from Webcams and timelaps, dependen on a .yaml config.
|
||||
|
||||
|
||||
## Sample cameras.yaml
|
||||
```yaml
|
||||
- name: "Dummy" # name of camera
|
||||
url: "" # camera-image url
|
||||
interval: 5 # Capture intervall in minutes
|
||||
timelapse: True # create timelapse every night
|
||||
website:
|
||||
picture: True # display camera on website
|
||||
timelapse: True # display timelapse on website
|
||||
|
||||
- name: "Dummy"
|
||||
url: ""
|
||||
interval: 5
|
||||
timelapse: True
|
||||
website:
|
||||
picture: True
|
||||
timelapse: True
|
||||
|
||||
```
|
||||
|
||||
## Sample docker-compose.yml
|
||||
```yaml
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
cam-flask:
|
||||
build: ./website
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
- ./cam/logs:/logs:rw
|
||||
- ./cam/output:/static/output:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- TZ="Europe/Berlin"
|
||||
|
||||
command: gunicorn -w 1
|
||||
-b :8000 app:app \
|
||||
--access-logfile ./logs/log.txt \
|
||||
--log-level info \
|
||||
--timeout 90 \
|
||||
--workers 25 \
|
||||
--worker-class gevent
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.cam.rule=Host(`cam.domain.com`)"
|
||||
- "traefik.http.routers.cam.entrypoints=websecure"
|
||||
- "traefik.http.services.cam.loadbalancer.server.port=8000"
|
||||
- "traefik.http.routers.cam.service=cam"
|
||||
- "traefik.http.routers.cam.tls.certresolver=production"
|
||||
networks:
|
||||
- traefik_default
|
||||
|
||||
|
||||
cam-crawler:
|
||||
build: ./crawler
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
- ./cam/timelapse:/archive:rw
|
||||
- ./cam/output:/output:rw
|
||||
- ./cam/config:/config:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
command: python app.py
|
||||
|
||||
networks:
|
||||
traefik_default:
|
||||
external: true
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
from flask import Flask, send_file, render_template, request, abort
|
||||
from prometheus_flask_exporter import PrometheusMetrics
|
||||
|
||||
import logging
|
||||
import time
|
||||
import os
|
||||
import yaml
|
||||
|
||||
os.environ['TZ'] = "Europe/Berlin"
|
||||
time.tzset()
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
filename='logs/flask.log',
|
||||
encoding='utf-8',
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s %(levelname)s:%(message)s'
|
||||
)
|
||||
|
||||
with open("config/cameras.yaml", "r") as yamlfile:
|
||||
config = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
||||
print("camera config read successful")
|
||||
#print(yaml)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
metrics = PrometheusMetrics(app)
|
||||
|
||||
metrics.info('app_info', 'Application info', version='1.0.3')
|
||||
|
||||
#@app.errorhandler(500)
|
||||
#def internal_error(error):
|
||||
#
|
||||
# return "500 error"
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
filename = 'static/404.jpg'
|
||||
return send_file(filename, mimetype='image/jpg'), 404
|
||||
|
||||
@app.route('/error',methods = ['POST', 'GET'])
|
||||
def errorTest():
|
||||
abort(500)
|
||||
|
||||
@app.route('/',methods = ['POST', 'GET'])
|
||||
def index():
|
||||
return render_template('index.html', config=config)
|
||||
|
||||
#@app.route('/BirdStalker')
|
||||
#def get_image():
|
||||
# filename = 'static/BirdStalker.jpg'
|
||||
# return send_file(filename, mimetype='image/jpg')
|
||||
|
||||
|
||||
|
||||
# main driver function
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
- name: "Dummy" # name of camera
|
||||
url: "" # camera-image url
|
||||
interval: 5 # Capture intervall in minutes
|
||||
timelapse: True # create timelapse every night
|
||||
website:
|
||||
picture: True # display camera on website
|
||||
timelapse: True # display timelapse on website
|
||||
|
||||
- name: "Dummy"
|
||||
url: ""
|
||||
interval: 5
|
||||
timelapse: True
|
||||
website:
|
||||
picture: True
|
||||
timelapse: True
|
||||
@@ -0,0 +1,5 @@
|
||||
Flask
|
||||
gunicorn
|
||||
pyyaml
|
||||
requests
|
||||
prometheus-flask-exporter
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 408 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 766 KiB |
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-bs-theme="dark">
|
||||
<head>
|
||||
|
||||
<meta charset='UTF-8'>
|
||||
<title>{% block title %}Birdstalker{% endblock %}</title>
|
||||
|
||||
<meta name="robots" content="noindex">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
|
||||
<link rel="icon" type="image/vnd.icon" href="/static/favicon.ico">
|
||||
|
||||
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.0/css/all.min.css'>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
{% for camera in config%}
|
||||
{% if camera.website.picture %}
|
||||
<div class="webcam">
|
||||
<h1>{{ camera.name }}</h1>
|
||||
<img src="/static/output/{{camera.name}}.jpg" />
|
||||
|
||||
{% if camera.website.timelapse and camera.timelapse %}
|
||||
<h2>Gestern:</h2>
|
||||
<video controls width="100%">
|
||||
<source src="/static/output/{{camera.name}}.mp4" type="video/mp4" />
|
||||
|
||||
Download the
|
||||
<a href="/static/output/{{camera.name}}.mp4">MP4</a>
|
||||
video.
|
||||
</video>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
video {
|
||||
background-color: hsla(0,0%,0%,.3);
|
||||
}
|
||||
|
||||
body {
|
||||
color: black;
|
||||
background-color: white ;
|
||||
line-height: 5px;
|
||||
font-family: Arial, sans-serif;
|
||||
background: url(/static/images/background.svg) no-repeat center center fixed;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
a {
|
||||
line-height:normal;
|
||||
}
|
||||
|
||||
.webcam {
|
||||
max-width: 900px;
|
||||
background-color: hsla(40,70%,60%,.9);
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-radius: 9px;
|
||||
border: 1px solid black;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.webcam img {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
</script>
|
||||
@@ -0,0 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM python:3.10
|
||||
|
||||
|
||||
COPY app/ .
|
||||
|
||||
#COPY requirements.txt requirements.txt
|
||||
#RUN apt-get update && apt-get install ffmpeg -y
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
#CMD ["gunicorn" , "-b", "0.0.0.0:8000" , "app:app"]
|
||||
Reference in New Issue
Block a user