first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
venv
|
||||||
|
__pycache__
|
||||||
|
data/*
|
||||||
@@ -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,7 @@
|
|||||||
|
venv
|
||||||
|
__pycache__
|
||||||
|
timelapse
|
||||||
|
static/output
|
||||||
|
archive
|
||||||
|
output
|
||||||
|
app/config/cameras.yaml
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# CameraCrawler
|
||||||
|
|
||||||
|
This is a python script, to crawl images from network cameras and creat a timelapse every night.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
image: registry.mnl-fhrn.de/lsrg/camerawebsite:latest
|
||||||
|
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:
|
||||||
|
image: registry.mnl-fhrn.de/lsrg/cameracrawler:latest
|
||||||
|
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,114 @@
|
|||||||
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
from PIL import Image
|
||||||
|
import requests
|
||||||
|
import atexit
|
||||||
|
import yaml
|
||||||
|
import timelaps
|
||||||
|
|
||||||
|
os.environ['TZ'] = "Europe/Berlin"
|
||||||
|
time.tzset()
|
||||||
|
|
||||||
|
sched = BackgroundScheduler(daemonic=True)
|
||||||
|
|
||||||
|
with open("config/cameras.yaml", "r") as yamlfile:
|
||||||
|
yaml = yaml.load(yamlfile, Loader=yaml.FullLoader)
|
||||||
|
print("camera config read successful")
|
||||||
|
#print(yaml)
|
||||||
|
|
||||||
|
def compress(src,dst):
|
||||||
|
|
||||||
|
filepath = os.path.join(os.getcwd(), src)
|
||||||
|
|
||||||
|
image = Image.open(filepath)
|
||||||
|
|
||||||
|
image = image.resize((image.width // 2, image.height // 2), Image.LANCZOS)
|
||||||
|
|
||||||
|
image.save(src,
|
||||||
|
"JPEG",
|
||||||
|
optimize = True,
|
||||||
|
quality = 90)
|
||||||
|
|
||||||
|
shutil.copyfile(src, dst)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def crawl_image(url, name):
|
||||||
|
ct = datetime.datetime.now()
|
||||||
|
|
||||||
|
file_path = "archive/" + name + "/" + ct.strftime("%Y%m%d")
|
||||||
|
file_name = name + "_" + ct.strftime("%Y%m%d_%H%M%S") + ".jpg"
|
||||||
|
|
||||||
|
staticPath = "output"
|
||||||
|
|
||||||
|
print('Started Image Downloaded: ',url)
|
||||||
|
Path(file_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
Path(staticPath).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Download image
|
||||||
|
try:
|
||||||
|
res = requests.get(url, stream = True, auth=('admin', 'hackerspace'))
|
||||||
|
if res.status_code == 200:
|
||||||
|
with open(os.path.join(file_path,file_name),'wb') as f:
|
||||||
|
shutil.copyfileobj(res.raw, f)
|
||||||
|
print('Image sucessfully Downloaded: ',os.path.join(file_path,file_name))
|
||||||
|
compress(os.path.join(file_path,file_name), staticPath + "/" + name + ".jpg")
|
||||||
|
|
||||||
|
original_size = os.path.getsize(os.path.join(file_path,file_name))
|
||||||
|
compressed_size = os.path.getsize(staticPath + "/" + name + ".jpg")
|
||||||
|
|
||||||
|
print("Original Size: ", original_size)
|
||||||
|
print("Compressed Size: ", compressed_size)
|
||||||
|
print("New image at:-", ct)
|
||||||
|
print()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("URL - Error:", url)
|
||||||
|
return -1
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def createTimelapsYesterday(name):
|
||||||
|
yesterday = datetime.datetime.now()- datetime.timedelta(days=1)
|
||||||
|
timelaps.createTimelaps(name=name,date=yesterday.strftime("%Y%m%d"))
|
||||||
|
|
||||||
|
|
||||||
|
def addJob(name, url, minutes):
|
||||||
|
sched.start()
|
||||||
|
|
||||||
|
#create backgroundTask for each camera
|
||||||
|
for camera in yaml:
|
||||||
|
sched.add_job(crawl_image,'interval',[camera["url"], camera["name"]],minutes=camera["interval"],max_instances=1, id=camera["name"] + " - crawler")
|
||||||
|
crawl_image(camera["url"], camera["name"])
|
||||||
|
if camera["timelapse"]:
|
||||||
|
sched.add_job(createTimelapsYesterday,'cron',[camera["name"]], hour='01',max_instances=1, id=camera["name"] + " - timelapse")
|
||||||
|
createTimelapsYesterday(camera["name"])
|
||||||
|
|
||||||
|
#atexit.register(lambda: scheduler.shutdown())
|
||||||
|
print(camera["name"], "--- was added with", camera["interval"], "minutes interval")
|
||||||
|
|
||||||
|
|
||||||
|
def initCrawler():
|
||||||
|
atexit.register(stopCrawler)
|
||||||
|
url_kantine = "http://192.168.187.80/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=wuuPhkmUCeI9WG7C&user=smart&password=DwyU8AfK2p2PKT"
|
||||||
|
addJob("kantine", url_kantine, 5)
|
||||||
|
|
||||||
|
|
||||||
|
def stopCrawler():
|
||||||
|
sched.shutdown(wait=True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
initCrawler()
|
||||||
|
while(True):
|
||||||
|
time.sleep(1)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -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,4 @@
|
|||||||
|
APScheduler
|
||||||
|
pillow
|
||||||
|
requests
|
||||||
|
pyyaml
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#import numpy as np
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
def createTimelaps(name, date = None, path = None):
|
||||||
|
staticPath = "output"
|
||||||
|
if date:
|
||||||
|
path = 'archive/' + name + '/' + date
|
||||||
|
elif path == None:
|
||||||
|
print("Error-Timelapse-missing args for folder")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print("Error-Timelapse-can't finde folder", path)
|
||||||
|
return -2
|
||||||
|
|
||||||
|
## Check if folder exists and is not empty
|
||||||
|
if os.path.exists(path) and not os.path.isfile(path):
|
||||||
|
if not os.listdir(path):
|
||||||
|
print("Error-Timelapse-Empty directory", path)
|
||||||
|
return -3
|
||||||
|
|
||||||
|
#print('creating writer')
|
||||||
|
#videoSize = (
|
||||||
|
# img_array[0].shape[1],
|
||||||
|
# img_array[0].shape[0]
|
||||||
|
#)
|
||||||
|
#writer = cv2.VideoWriter( path + '/' + name + '.mp4', cv2.VideoWriter_fourcc(*"mp4v"), 10, videoSize)
|
||||||
|
#print('writer created')
|
||||||
|
#print()
|
||||||
|
|
||||||
|
|
||||||
|
print('creating video')
|
||||||
|
|
||||||
|
if os.path.exists(path + "/" + name + ".mp4"):
|
||||||
|
print("video allready exists")
|
||||||
|
os.remove(path + "/" + name + ".mp4")
|
||||||
|
imgWidht = 2304
|
||||||
|
imgHeight = 864
|
||||||
|
os.system('ffmpeg -hide_banner -loglevel error -stats -framerate 10 -pattern_type glob -i "' + path + '/*.jpg" -s:v ' + str(imgWidht) + 'x' + str(imgHeight) + ' -c:v libx264 -crf 17 -pix_fmt yuv420p ' + path + '/' + name + '.mp4')
|
||||||
|
|
||||||
|
shutil.copyfile(path + "/" + name + ".mp4", staticPath + "/" + name + ".mp4")
|
||||||
|
print('video created')
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.make_archive(path, 'zip', path)
|
||||||
|
print('zip created')
|
||||||
|
except:
|
||||||
|
print('Error while creating .zip')
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
print('folder cleared')
|
||||||
|
except:
|
||||||
|
print('Error while removing folder')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
createTimelaps("kantine", path="timelapse/kantine/20230828")
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM python:3.11
|
||||||
|
|
||||||
|
|
||||||
|
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"]
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
version: '3.4'
|
||||||
|
|
||||||
|
services:
|
||||||
|
birdstalker-flask:
|
||||||
|
build: ./website
|
||||||
|
restart: unless-stopped
|
||||||
|
stdin_open: true # docker run -i
|
||||||
|
tty: true # docker run -t
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./data/logs:/logs:rw
|
||||||
|
- ./data/config:/config:ro
|
||||||
|
- ./data/output:/static/output:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
#ports:
|
||||||
|
# - 5020:8000
|
||||||
|
environment:
|
||||||
|
- TZ="Europe/Berlin"
|
||||||
|
#command: python app.py
|
||||||
|
|
||||||
|
command: gunicorn -w 1
|
||||||
|
-b :8000 app:app \
|
||||||
|
--access-logfile ./logs/log.txt \
|
||||||
|
--log-level info \
|
||||||
|
--timeout 90 \
|
||||||
|
--workers 25 \
|
||||||
|
--worker-class gevent \
|
||||||
|
--debug=True
|
||||||
|
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.cameraWebsite.rule=Host(`camera.manuel-fehren.de`)"
|
||||||
|
- "traefik.http.routers.cameraWebsite.entrypoints=websecure"
|
||||||
|
- "traefik.http.services.cameraWebsite.loadbalancer.server.port=8000"
|
||||||
|
- "traefik.http.routers.cameraWebsite.service=cameraWebsite"
|
||||||
|
- "traefik.http.routers.cameraWebsite.tls.certresolver=production"
|
||||||
|
networks:
|
||||||
|
- traefik-net
|
||||||
|
|
||||||
|
|
||||||
|
bird-stalker-crawler:
|
||||||
|
build: ./crawler
|
||||||
|
restart: unless-stopped
|
||||||
|
stdin_open: true # docker run -i
|
||||||
|
tty: true # docker run -t
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./data/timelapse:/archive:rw
|
||||||
|
- ./data/output:/output:rw
|
||||||
|
- ./data/config:/config:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
command: python app.py
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-net:
|
||||||
|
external: true
|
||||||
@@ -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