async def zipped(self, camera_id, group): key = self.get_argument('key', None) camera_config = config.get_camera(camera_id) if key: logging.debug('serving zip file for group "%(group)s" of camera %(id)s with key %(key)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'key': key}) if utils.is_local_motion_camera(camera_config): data = mediafiles.get_prepared_cache(key) if not data: logging.error('prepared cache data for key "%s" does not exist' % key) raise HTTPError(404, 'no such key') pretty_filename = camera_config['camera_name'] + '_' + group pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename) self.set_header('Content-Type', 'application/zip') self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + '.zip;') return self.finish(data) elif utils.is_remote_camera(camera_config): resp = await remote.get_zipped_content(camera_config, media_type='picture', key=key, group=group) if resp.error: return self.finish_json({'error': 'Failed to download zip file from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}}) self.set_header('Content-Type', resp.result['content_type']) self.set_header('Content-Disposition', resp.result['content_disposition']) return self.finish(resp.result['data']) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') else: # prepare logging.debug('preparing zip file for group "%(group)s" of camera %(id)s' % { 'group': group or 'ungrouped', 'id': camera_id}) if utils.is_local_motion_camera(camera_config): data = await mediafiles.get_zipped_content(camera_config, media_type='picture', group=group) if data is None: return self.finish_json({'error': 'Failed to create zip file.'}) key = mediafiles.set_prepared_cache(data) logging.debug('prepared zip file for group "%(group)s" of camera %(id)s with key %(key)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'key': key}) self.finish_json({'key': key}) elif utils.is_remote_camera(camera_config): resp = await remote.make_zipped_content(camera_config, media_type='picture', group=group) if resp.error: return self.finish_json({'error': 'Failed to make zip file at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}}) return self.finish_json({'key': resp.result['key']}) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def list(self, camera_id): logging.debug('listing movies for camera %(id)s' % {'id': camera_id}) camera_config = config.get_camera(camera_id) if utils.is_local_motion_camera(camera_config): media_list = await mediafiles.list_media(camera_config, media_type='movie', prefix=self.get_argument( 'prefix', None)) if media_list is None: self.finish_json({'error': 'Failed to get movies list.'}) return self.finish_json({ 'mediaList': media_list, 'cameraName': camera_config['camera_name'] }) elif utils.is_remote_camera(camera_config): resp = await remote.list_media(camera_config, media_type='movie', prefix=self.get_argument( 'prefix', None)) if resp.error: return self.finish_json({ 'error': 'Failed to get movie list for %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error } }) return self.finish_json(resp.media_list) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def delete_all(self, camera_id, group): logging.debug('deleting movie group "%(group)s" of camera %(id)s' % { 'group': group or 'ungrouped', 'id': camera_id }) camera_config = config.get_camera(camera_id) if utils.is_local_motion_camera(camera_config): try: mediafiles.del_media_group(camera_config, group, 'movie') return self.finish_json() except Exception as e: return self.finish_json({'error': str(e)}) elif utils.is_remote_camera(camera_config): resp = await remote.del_media_group(camera_config, group=group, media_type='movie') if resp.error: return self.finish_json({ 'error': 'Failed to delete movie group at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error } }) return self.finish_json() else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def delete(self, camera_id, filename): logging.debug('deleting movie %(filename)s of camera %(id)s' % { 'filename': filename, 'id': camera_id }) camera_config = config.get_camera(camera_id) if utils.is_local_motion_camera(camera_config): try: mediafiles.del_media_content(camera_config, filename, 'movie') return self.finish_json() except Exception as e: return self.finish_json({'error': str(e)}) elif utils.is_remote_camera(camera_config): resp = await remote.del_media_content(camera_config, filename=filename, media_type='movie') if resp.error: return self.finish_json({ 'error': 'Failed to delete movie from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error } }) return self.finish_json() else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def download(self, camera_id, filename): logging.debug('downloading picture %(filename)s of camera %(id)s' % { 'filename': filename, 'id': camera_id}) camera_config = config.get_camera(camera_id) if utils.is_local_motion_camera(camera_config): content = mediafiles.get_media_content(camera_config, filename, 'picture') pretty_filename = camera_config['camera_name'] + '_' + os.path.basename(filename) self.set_header('Content-Type', 'image/jpeg') self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';') return self.finish(content) elif utils.is_remote_camera(camera_config): resp = await remote.get_media_content(camera_config, filename=filename, media_type='picture') if resp.error: return self.finish_json({'error': 'Failed to download picture from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}}) pretty_filename = os.path.basename(filename) # no camera name available w/o additional request self.set_header('Content-Type', 'image/jpeg') self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';') return self.finish(resp.result) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def get_config(self, camera_id): if camera_id: logging.debug('getting config for camera %(id)s' % {'id': camera_id}) if camera_id not in config.get_camera_ids(): raise HTTPError(404, 'no such camera') local_config = config.get_camera(camera_id) if utils.is_local_motion_camera(local_config): ui_config = config.motion_camera_dict_to_ui(local_config) return self.finish_json(ui_config) elif utils.is_remote_camera(local_config): resp = await remote.get_config(local_config) if resp.error: msg = 'Failed to get remote camera configuration for %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(local_config), 'msg': resp.error } return self.finish_json_with_error(msg) for key, value in list(local_config.items()): resp.remote_ui_config[key.replace('@', '')] = value # replace the real device url with the remote camera path resp.remote_ui_config['device_url'] = remote.pretty_camera_url( local_config) return self.finish_json(resp.remote_ui_config) else: # assuming simple mjpeg camera ui_config = config.simple_mjpeg_camera_dict_to_ui(local_config) return self.finish_json(ui_config) else: logging.debug('getting main config') ui_config = config.main_dict_to_ui(config.get_main()) return self.finish_json(ui_config)
async def post(self, camera_id, action): camera_id = int(camera_id) if camera_id not in config.get_camera_ids(): raise HTTPError(404, 'no such camera') local_config = config.get_camera(camera_id) if utils.is_remote_camera(local_config): resp = await remote.exec_action(local_config, action) if resp.error: msg = 'Failed to execute action on remote camera at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(local_config), 'msg': resp.error } return self.finish_json({'error': msg}) return self.finish_json() if action == 'snapshot': logging.debug('executing snapshot action for camera with id %s' % camera_id) await self.snapshot(camera_id) return elif action == 'record_start': logging.debug( 'executing record_start action for camera with id %s' % camera_id) return self.record_start(camera_id) elif action == 'record_stop': logging.debug( 'executing record_stop action for camera with id %s' % camera_id) return self.record_stop(camera_id) action_commands = config.get_action_commands(local_config) command = action_commands.get(action) if not command: raise HTTPError(400, 'unknown action') logging.debug('executing %s action for camera with id %s: "%s"' % (action, camera_id, command)) self.run_command_bg(command)
def _handle_get_config_response(self, camera_id, local_config, resp: utils.GetConfigResponse, cameras: list, length: list) -> None: if resp.error: cameras.append({ 'id': camera_id, 'name': '<' + remote.pretty_camera_url(local_config) + '>', 'enabled': False, 'streaming_framerate': 1, 'framerate': 1 }) else: resp.remote_ui_config['id'] = camera_id if not resp.remote_ui_config['enabled'] and local_config[ '@enabled']: # if a remote camera is disabled, make sure it's disabled locally as well local_config['@enabled'] = False config.set_camera(camera_id, local_config) elif resp.remote_ui_config[ 'enabled'] and not local_config['@enabled']: # if a remote camera is locally disabled, make sure the remote config says the same thing resp.remote_ui_config['enabled'] = False for key, value in list(local_config.items()): resp.remote_ui_config[key.replace('@', '')] = value cameras.append(resp.remote_ui_config) finished = self.check_finished(cameras, length) return
async def get(self, camera_id, filename=None, include_body=True): logging.debug('downloading movie %(filename)s of camera %(id)s' % { 'filename': filename, 'id': camera_id }) self.pretty_filename = os.path.basename(filename) if camera_id is not None: camera_id = int(camera_id) if camera_id not in config.get_camera_ids(): raise HTTPError(404, 'no such camera') camera_config = config.get_camera(camera_id) if utils.is_local_motion_camera(camera_config): filename = mediafiles.get_media_path(camera_config, filename, 'movie') self.pretty_filename = camera_config[ 'camera_name'] + '_' + self.pretty_filename await StaticFileHandler.get(self, filename, include_body=include_body) return elif utils.is_remote_camera(camera_config): # we will cache the movie since it takes a while to fetch from the remote camera # and we may be going to play it back in the browser, which will fetch the video in chunks tmpfile = self.tmpdir + '/' + self.pretty_filename if os.path.isfile(tmpfile): # have a cached copy, update the timestamp so it's not flushed import time mtime = os.stat(tmpfile).st_mtime os.utime(tmpfile, (time.time(), mtime)) await StaticFileHandler.get(self, tmpfile, include_body=include_body) return resp = await remote.get_media_content(camera_config, filename, media_type='movie') if resp.error: return self.finish_json({ 'error': 'Failed to download movie from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error } }) # check if the file has been created by another request while we were fetching the movie if not os.path.isfile(tmpfile): tmp = open(tmpfile, 'wb') tmp.write(resp.result) tmp.close() await StaticFileHandler.get(self, tmpfile, include_body=include_body) return else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')
async def timelapse(self, camera_id, group): key = self.get_argument('key', None) check = self.get_argument('check', False) camera_config = config.get_camera(camera_id) if key: # download logging.debug('serving timelapse movie for group "%(group)s" of camera %(id)s with key %(key)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'key': key}) if utils.is_local_motion_camera(camera_config): data = mediafiles.get_prepared_cache(key) if data is None: logging.error('prepared cache data for key "%s" does not exist' % key) raise HTTPError(404, 'no such key') pretty_filename = camera_config['camera_name'] + '_' + group pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename) pretty_filename += '.' + mediafiles.FFMPEG_EXT_MAPPING.get(camera_config['movie_codec'], 'avi') self.set_header('Content-Type', 'video/x-msvideo') self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';') return self.finish(data) elif utils.is_remote_camera(camera_config): resp = await remote.get_timelapse_movie(camera_config, key, group=group) if resp.error: msg = 'Failed to download timelapse movie from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error} return self.finish_json({'error': msg}) self.set_header('Content-Type', resp.result['content_type']) self.set_header('Content-Disposition', resp.result['content_disposition']) return self.finish(resp.result['data']) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') elif check: logging.debug('checking timelapse movie status for group "%(group)s" of camera %(id)s' % { 'group': group or 'ungrouped', 'id': camera_id}) if utils.is_local_motion_camera(camera_config): status = mediafiles.check_timelapse_movie() if status['progress'] == -1 and status['data']: key = mediafiles.set_prepared_cache(status['data']) logging.debug('prepared timelapse movie for group "%(group)s" of camera %(id)s with key %(key)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'key': key}) return self.finish_json({'key': key, 'progress': -1}) else: return self.finish_json(status) elif utils.is_remote_camera(camera_config): resp = await remote.check_timelapse_movie(camera_config, group=group) if resp.error: msg = 'Failed to check timelapse movie progress at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': resp.error} return self.finish_json({'error': msg}) if resp.result['progress'] == -1 and resp.result.get('key'): self.finish_json({'key': resp.result['key'], 'progress': -1}) else: self.finish_json(resp.result) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') else: # start timelapse interval = int(self.get_argument('interval')) framerate = int(self.get_argument('framerate')) msg = 'preparing timelapse movie for group "%(group)s" of camera %(id)s with rate %(framerate)s/%(int)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'framerate': framerate, 'int': interval} logging.debug(msg) if utils.is_local_motion_camera(camera_config): status = mediafiles.check_timelapse_movie() if status['progress'] != -1: return self.finish_json({'progress': status['progress']}) # timelapse already active else: mediafiles.make_timelapse_movie(camera_config, framerate, interval, group=group) return self.finish_json({'progress': -1}) elif utils.is_remote_camera(camera_config): check_timelapse_resp = await remote.check_timelapse_movie(camera_config, group=group) if check_timelapse_resp.error: return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': check_timelapse_resp.error}}) if check_timelapse_resp.result['progress'] != -1: # timelapse already active return self.finish_json({'progress': check_timelapse_resp.result['progress']}) make_timelapse_resp = await remote.make_timelapse_movie(camera_config, framerate, interval, group=group) if make_timelapse_resp.error: return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': make_timelapse_resp.error}}) return self.finish_json({'progress': -1}) else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation')