api.py (3654B)
1 # api.py 2 # 3 # Copyright (C) 2017 Brian C. Lane 4 # 5 # This program is free software; you can redistribute it and/or modify 6 # it under the terms of the GNU General Public License as published by 7 # the Free Software Foundation; either version 2 of the License, or 8 # (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public License 16 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 from gevent import monkey; monkey.patch_all() 18 from datetime import datetime 19 from glob import glob 20 import os 21 22 # Fix mimetypes so that it recognized m4v as video/mp4 23 import mimetypes 24 mimetypes.add_type("video/mp4", ".m4v") 25 26 import bottle 27 from bottle import abort, install, route, run, static_file, request, Response, JSONPlugin 28 from bottle import template 29 from json import dumps 30 from threading import Thread 31 32 from . import logger 33 from .events import camera_events, EventCache, queue_events 34 35 bottle.TEMPLATE_PATH.insert(0, os.path.dirname(__file__)+"/ui/") 36 37 TIME_FORMAT = "%Y-%m-%d %H:%M:%S" 38 def timestr_to_dt(rfc_str): 39 return datetime.strptime(rfc_str, TIME_FORMAT) 40 41 def run_api(logging_queue, base_dir, cameras, host, port, debug, queue_rx): 42 log = logger.log(logging_queue) 43 log.info("Starting API", base_dir=base_dir, cameras=cameras, host=host, port=port, debug=debug) 44 EventCache.logger(log) 45 46 # Listen to queue_rx for new events 47 th = Thread(target=queue_events, args=(log, queue_rx)) 48 th.start() 49 50 @route('/') 51 @route('/<filename>') 52 def serve_root(filename="index.html"): 53 return static_file(filename, root=os.path.dirname(__file__)+"/ui") 54 55 @route('/motion/<filepath:path>') 56 def serve_motion(filepath): 57 if os.path.isfile(base_dir + "/" + filepath): 58 return static_file(filepath, root=base_dir) 59 60 path = os.path.normpath(base_dir + os.path.normpath("/" + filepath)) 61 if not os.path.isdir(path): 62 abort(404) 63 64 listing = sorted([os.path.basename(f) for f in glob(path + "/*.jpg")]) 65 return template("dirlist.tmpl", listing=listing) 66 67 @route('/api/cameras/list') 68 def serve_cameras_list() -> Response: 69 return {"cameras": cameras} 70 71 @route('/api/events/<cameras>') 72 def serve_events(cameras): 73 # request.query is a bottle.MultiDict which pylint doesn't understand 74 # pylint: disable=no-member 75 start = timestr_to_dt(request.query.get("start", "1985-10-26 01:22:00")) 76 end = timestr_to_dt(request.query.get("end", datetime.now().strftime(TIME_FORMAT))) 77 offset= int(request.query.get("offset", "0")) 78 limit = int(request.query.get("limit", "10")) 79 camera_list = cameras.split(",") 80 # log.debug("serve_events", camera_list=camera_list, start=str(start), end=str(end), offset=offset, limit=limit) 81 82 events = {} 83 for camera in camera_list: 84 events[camera] = camera_events(log, base_dir, camera, start, end, offset, limit) 85 86 # log.debug("serve_events", events=events) 87 return {"start": str(start), 88 "end": str(end), 89 "offset": offset, 90 "limit": limit, 91 "events": events} 92 93 # Use str as default in json dumps for objects like datetime 94 install(JSONPlugin(json_dumps=lambda s: dumps(s, default=str))) 95 run(host=host, port=port, debug=debug, server="gevent") 96 97 th.join(30)