api.py (3604B)
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 from bottle import abort, install, route, run, static_file, request, Response, JSONPlugin 27 from bottle import template 28 from json import dumps 29 from threading import Thread 30 31 from . import logger 32 from .events import camera_events, EventCache, queue_events 33 34 TIME_FORMAT = "%Y-%m-%d %H:%M:%S" 35 def timestr_to_dt(rfc_str): 36 return datetime.strptime(rfc_str, TIME_FORMAT) 37 38 def run_api(logging_queue, base_dir, cameras, host, port, debug, queue_rx): 39 log = logger.log(logging_queue) 40 log.info("Starting API", base_dir=base_dir, cameras=cameras, host=host, port=port, debug=debug) 41 EventCache.logger(log) 42 43 # Listen to queue_rx for new events 44 th = Thread(target=queue_events, args=(log, queue_rx)) 45 th.start() 46 47 @route('/') 48 @route('/<filename>') 49 def serve_root(filename="index.html"): 50 return static_file(filename, root=os.path.dirname(__file__)+"/ui") 51 52 @route('/motion/<filepath:path>') 53 def serve_motion(filepath): 54 if os.path.isfile(base_dir + "/" + filepath): 55 return static_file(filepath, root=base_dir) 56 57 path = os.path.normpath(base_dir + os.path.normpath("/" + filepath)) 58 if not os.path.isdir(path): 59 abort(404) 60 61 listing = sorted([os.path.basename(f) for f in glob(path + "/*.jpg")]) 62 return template(os.path.dirname(__file__)+"/ui/dirlist.tmpl", listing=listing) 63 64 @route('/api/cameras/list') 65 def serve_cameras_list() -> Response: 66 return {"cameras": cameras} 67 68 @route('/api/events/<cameras>') 69 def serve_events(cameras): 70 # request.query is a bottle.MultiDict which pylint doesn't understand 71 # pylint: disable=no-member 72 start = timestr_to_dt(request.query.get("start", "1985-10-26 01:22:00")) 73 end = timestr_to_dt(request.query.get("end", datetime.now().strftime(TIME_FORMAT))) 74 offset= int(request.query.get("offset", "0")) 75 limit = int(request.query.get("limit", "10")) 76 camera_list = cameras.split(",") 77 # log.debug("serve_events", camera_list=camera_list, start=str(start), end=str(end), offset=offset, limit=limit) 78 79 events = {} 80 for camera in camera_list: 81 events[camera] = camera_events(log, base_dir, camera, start, end, offset, limit) 82 83 # log.debug("serve_events", events=events) 84 return {"start": str(start), 85 "end": str(end), 86 "offset": offset, 87 "limit": limit, 88 "events": events} 89 90 # Use str as default in json dumps for objects like datetime 91 install(JSONPlugin(json_dumps=lambda s: dumps(s, default=str))) 92 run(host=host, port=port, debug=debug, server="gevent") 93 94 th.join(30)