EE-downloader-v3/app.py

434 lines
13 KiB
Python
Raw Permalink Normal View History

import json, requests
from flask import Flask, request, session, render_template, redirect, url_for, Response, send_file
from flask_session import Session
import pandas as pd
import numpy as np
from matplotlib.figure import Figure
import matplotlib.dates as mdates
import base64
from tqdm import tqdm
2023-10-06 02:18:39 +00:00
import datetime
from io import BytesIO
import logging
logger = logging.getLogger()
from functools import wraps
from EagleEyev3 import *
from settings import config
SECRET_KEY = "this needs to be changed to something unique and not checked into git"
# check if it could pull in a config object from settings.py
if config:
# start checking for keys in config object and set sensible defaults
if 'days_of_history' in config:
DAYS_OF_HISTORY = config['days_of_history']
else:
# fallback to a sane default
DAYS_OF_HISTORY = 1
if 'log_level' in config:
logger.setLevel(config['log_level'])
2023-10-06 02:18:39 +00:00
else:
logging.setLevel('INFO')
if 'client_secret' in config:
SECRET_KEY = config['client_secret']
else:
2023-10-06 02:18:39 +00:00
logging.setLevel('INFO')
logging.info(f"Using EagleEyev3 version {EagleEyev3.__version__}")
app = Flask(__name__)
app.secret_key = SECRET_KEY
SESSION_TYPE = 'filesystem'
SESSION_PERMANENT = False
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # one week in seconds
app.config.from_object(__name__)
Session(app)
2023-10-06 02:18:39 +00:00
from flask_sqlalchemy import SQLAlchemy
from models import *
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db.init_app(app)
with app.app_context():
db.create_all()
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
logging.debug(session)
if 'een' in session and session['een'].access_token:
logging.debug(f"@login_required access_token: {session['een'].access_token}")
return f(*args, **kwargs)
else:
# failed to find a valid session so redirecting to landing page
if 'een' in session:
logging.debug(f"@login_required access_token: {session['een'].access_token}")
else:
logging.warn('@login_requried failed to find a valid session')
return redirect(url_for('landing'))
return decorated_function
@app.route('/landing')
def landing():
# sometimes you just want to get to the landing page and want to avoid the index redirect logic
if 'een' in session:
een = session['een']
else:
een = EagleEyev3(config)
session['een'] = een
base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize"
path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}"
values = { "login_link": f"{base_url}{path_url}" }
return render_template('cover.html', template_values=values)
@app.route('/')
@login_required
def index():
# get een instance from session, @login_required already checked for it
een = session['een']
# using current_user as a proxy for an established valid session
if een.access_token == None:
base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize"
path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}"
values = { "login_link": f"{base_url}{path_url}" }
return render_template('cover.html', template_values=values)
# call this to check if session is actually valid
check = een.get_current_user()
if 'success' not in check or check['success'] == False:
base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize"
path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}"
values = { "login_link": f"{base_url}{path_url}" }
return render_template('cover.html', template_values=values)
else:
logging.info(f"{check['success']} - check get_current_user {een.current_user['email']} {een.access_token}")
een.get_list_of_cameras()
een.get_list_of_accounts()
2023-10-06 02:18:39 +00:00
save_list_of_cameras(een=een)
if len(een.accounts) > 0 and een.active_account == None:
# they need to pick and account
logging.info(f"redirecting to accounts.html because they don't have an active account { een.active_account }")
values = {
"current_user": een.current_user,
"accounts": een.accounts,
"active_account": een.active_account,
"user_base_url": een.user_base_url,
"access_token": een.access_token
}
redirect(url_for('accounts'))
values = {
"current_user": een.current_user,
"cameras": een.cameras,
"camera_count": len(een.cameras),
"camera_count_online": len([i for i in een.cameras if i.is_online()]),
"camera_count_offline": len([i for i in een.cameras if i.is_offline()]),
"accounts": een.accounts,
"active_account": een.active_account,
"user_base_url": een.user_base_url,
"access_token": een.access_token
}
return render_template('index.html', template_values=values)
@app.route('/login_callback')
def login_callback():
# This is getting the ?code= querystring value from the HTTP request.
code = request.args.get('code')
# create a new een object and store it in their session
een = EagleEyev3(config)
session['een'] = een
if (code):
# use the include code parameter to complete login process
oauth_object = een.login_tokens(code)
logging.debug(oauth_object)
2023-10-06 02:18:39 +00:00
# let's try resaving this to see if it fixs the missing access_token on first request after login
session['een'] = een
2023-10-06 02:18:39 +00:00
een.get_current_user()
try:
user = User.query.filter(User.email == een.current_user['email']).first()
if user is None:
logging.debug(f"no user found with email {een.current_user['email']}, creating new user")
user = User(account_id=een.current_user['accountId'],\
user_id=een.current_user['id'],\
email=een.current_user['email'],\
timezone=een.current_user['timeZone']['timeZone'],\
access_token=een.access_token,\
refresh_token=een.refresh_token,\
last_login=datetime.now())
else:
logging.debug(f"found user with {een.current_user['email']}, updating")
user.last_login = datetime.now()
user.access_token = een.access_token
user.refresh_token = een.refresh_token
logging.debug(f"saving user object")
user.save()
except Exception as e:
logging.warn(e)
return redirect(url_for('index'))
@app.route('/logout')
def logout():
2023-10-06 02:18:39 +00:00
if 'een' in session:
een = session['een']
logging.debug(f"calling logout { een.access_token = }")
een.logout()
session.pop('een')
return redirect(url_for('index'))
2023-10-06 02:18:39 +00:00
def save_list_of_cameras(een=None):
user = User.query.filter(User.email == een.current_user['email']).first()
if user:
user_id = user.id
else:
user_id = None
for c in een.cameras:
try:
check_camera = Camera.query.filter(Camera.device_id == c.id).first()
if check_camera is None:
logging.debug(f"no camera found for {c.id}, creating new camera")
check_camera = Camera(device_id=c.id,\
timezone=None, \
name=c.name,
user_id=user_id)
else:
logging.debug(f"found camera for {c.id}")
logging.debug(f"saving camera object")
check_camera.save()
except Exception as e:
logging.warn(e)
@app.route('/cameras')
@login_required
def cameras():
# get een instance from session, @login_required already checked for it
een = session['een']
logging.debug(een.get_list_of_cameras())
2023-10-06 02:18:39 +00:00
save_list_of_cameras(een=een)
values = {
"current_user": een.current_user,
"cameras": een.cameras,
"camera_count": len(een.cameras),
"camera_count_online": len([i for i in een.cameras if i.is_online()]),
"camera_count_offline": len([i for i in een.cameras if i.is_offline()])
}
return render_template('cameras_partial.html', template_values=values)
@app.route('/accounts')
@login_required
def accounts():
# get een instance from session, @login_required already checked for it
een = session['een']
logging.debug(een.get_list_of_accounts())
values = {
"current_user": een.current_user,
"accounts": een.accounts,
"active_account": een.active_account
}
return render_template('accounts_partial.html', template_values=values)
@app.route('/switch_account')
@login_required
def switch_account():
# This is getting the ?account= querystring value from the HTTP request.
account = request.args.get('account')
# get een instance from session, @login_required already checked for it
een = session['een']
if (account):
# switch into account
logging.info(f"attempting to switch into account {account}")
logging.debug(een.login_from_reseller(target_account_id=account))
return redirect(url_for('index'))
@app.route('/camera/<esn>/preview_image')
@login_required
def camera__preivew_image(esn=None):
# get een instance from session, @login_required already checked for it
een = session['een']
camera = een.get_camera_by_id(esn)
res = camera.get_live_preview()
if res:
return send_file(BytesIO(res.content), mimetype='image/jpeg')
else:
return send_file('static/placeholder.png')
@app.route('/camera/<esn>/preview')
@login_required
def camera_live_preivew(esn=None):
# get een instance from session, @login_required already checked for it
een = session['een']
camera = een.get_camera_by_id(esn)
values = {
"current_user": een.current_user,
"camera": camera,
"events": camera.events['status']
}
return render_template('camera_preview.html', template_values=values)
@app.route('/camera/<esn>/video_player/<start_timestamp>')
@login_required
def camera_video_player(esn=None, start_timestamp=None):
# get een instance from session, @login_required already checked for it
een = session['een']
camera = een.get_camera_by_id(esn)
2023-08-29 17:41:36 +00:00
try:
video_url = next(x['mp4Url'] for x in camera.videos if x['startTimestamp'] == start_timestamp)
except StopIteration:
logging.warn(f"couldn't find video with { start_timestamp } for camera { camera.id }")
values = {
"current_user": een.current_user,
"camera_name": camera.name,
"start_timestamp": start_timestamp,
"video_url": video_url
}
return render_template('camera_video_player.html', template_values=values)
@app.route('/camera/<esn>/videos')
@login_required
def camera_list_of_videos(esn=None):
# get een instance from session, @login_required already checked for it
een = session['een']
camera = een.get_camera_by_id(esn)
2023-10-06 02:18:39 +00:00
logging.debug(camera.get_list_of_videos(start_timestamp=een.time_before(hours=24 * DAYS_OF_HISTORY), end_timestamp=een.time_now()))
db_camera = Camera.query.filter(Camera.device_id == esn).first()
if db_camera:
for v in camera.videos:
try:
check_video = Download.query.filter(Download.camera_id == db_camera.id)\
.filter(Download.start_timestamp == datetime.fromisoformat(v['startTimestamp']))\
.first()
if check_video is None:
logging.debug(f"creating a new record for {db_camera.id} {v['startTimestamp']}")
new_video = Download(url=v['mp4Url'],\
camera_id=db_camera.id,\
start_timestamp=datetime.fromisoformat(v['startTimestamp']),\
end_timestamp=datetime.fromisoformat(v['endTimestamp']),\
status='new',
error=None)
new_video.save()
except Exception as e:
logging.warn(f"Exception saving videos to db: {e}")
values = {
"current_user": een.current_user,
"camera": { 'id': camera.id, 'name': camera.name },
"videos": camera.videos
}
if 'HX-Request' in request.headers:
return render_template('camera_videos_partial.html', template_values=values)
else:
return values
if __name__ == '__main__':
2023-10-06 02:18:39 +00:00
app.run(host=config['server_host'], port=config['server_port'])