From fd59ef7c3d0e79e809deb148b01620b4a43c29c3 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 12 Jan 2014 18:16:23 +0200 Subject: [PATCH] configuration refactoring --- src/config.py | 166 ++++++++++++++------------------ src/handlers.py | 239 ++++++++++------------------------------------ src/remote.py | 83 ++++++++++++---- src/utils.py | 11 +++ static/js/main.js | 113 ++++++++++++++-------- 5 files changed, 270 insertions(+), 342 deletions(-) diff --git a/src/config.py b/src/config.py index 5a8a69c..75f63cf 100644 --- a/src/config.py +++ b/src/config.py @@ -77,18 +77,18 @@ def get_main(as_lines=False): if as_lines: return lines - data = _conf_to_dict(lines, list_names=['thread']) - _set_default_motion(data) + main_config = _conf_to_dict(lines, list_names=['thread']) + _set_default_motion(main_config) - _main_config_cache = data + _main_config_cache = main_config - return data + return main_config -def set_main(data): +def set_main(main_config): global _main_config_cache - _set_default_motion(data) + _set_default_motion(main_config) config_file_path = os.path.join(settings.CONF_PATH, _MAIN_CONFIG_FILE_NAME) @@ -96,8 +96,8 @@ def set_main(data): lines = get_main(as_lines=True) # preserve the threads - if 'thread' not in data: - threads = data.setdefault('thread', []) + if 'thread' not in main_config: + threads = main_config.setdefault('thread', []) for line in lines: match = re.match('^\s*thread\s+([a-zA-Z0-9.\-]+)', line) if match: @@ -115,7 +115,7 @@ def set_main(data): raise - lines = _dict_to_conf(lines, data, list_names=['thread']) + lines = _dict_to_conf(lines, main_config, list_names=['thread']) try: file.writelines([l + '\n' for l in lines]) @@ -129,9 +129,9 @@ def set_main(data): finally: file.close() - _main_config_cache = data + _main_config_cache = main_config - return data + return main_config def get_camera_ids(): @@ -214,52 +214,52 @@ def get_camera(camera_id, as_lines=False): if as_lines: return lines - data = _conf_to_dict(lines) + camera_config = _conf_to_dict(lines) - data.setdefault('@proto', 'v4l2') + camera_config.setdefault('@proto', 'v4l2') # determine the enabled status - if data['@proto'] == 'v4l2': + if camera_config['@proto'] == 'v4l2': main_config = get_main() threads = main_config.get('thread', []) - data['@enabled'] = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id} in threads - data['@id'] = camera_id + camera_config['@enabled'] = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id} in threads + camera_config['@id'] = camera_id - _set_default_motion_camera(camera_id, data) + _set_default_motion_camera(camera_id, camera_config) if _camera_config_cache is None: _camera_config_cache = {} - _camera_config_cache[camera_id] = data + _camera_config_cache[camera_id] = camera_config - return data + return camera_config -def set_camera(camera_id, data): +def set_camera(camera_id, camera_config): global _camera_config_cache - if data['@proto'] == 'v4l2': - _set_default_motion_camera(camera_id, data) + if camera_config['@proto'] == 'v4l2': + _set_default_motion_camera(camera_id, camera_config) # set the enabled status in main config main_config = get_main() threads = main_config.setdefault('thread', []) config_file_name = _CAMERA_CONFIG_FILE_NAME % {'id': camera_id} - if data['@enabled'] and config_file_name not in threads: + if camera_config['@enabled'] and config_file_name not in threads: threads.append(config_file_name) - elif not data['@enabled']: + elif not camera_config['@enabled']: threads = [t for t in threads if t != config_file_name] main_config['thread'] = threads - data['@id'] = camera_id + camera_config['@id'] = camera_id set_main(main_config) # try to create the target_dir try: - os.makedirs(data['target_dir']) + os.makedirs(camera_config['target_dir']) except OSError as e: if e.errno != errno.EEXIST: @@ -286,7 +286,7 @@ def set_camera(camera_id, data): raise - lines = _dict_to_conf(lines, data) + lines = _dict_to_conf(lines, camera_config) try: file.writelines([l + '\n' for l in lines]) @@ -303,9 +303,9 @@ def set_camera(camera_id, data): if _camera_config_cache is None: _camera_config_cache = {} - _camera_config_cache[camera_id] = data + _camera_config_cache[camera_id] = camera_config - return data + return camera_config def add_camera(device_details): @@ -330,7 +330,7 @@ def add_camera(device_details): if proto == 'v4l2': data['@name'] = 'Camera' + str(camera_id) - data['videodevice'] = device_details['device'] + data['videodevice'] = device_details['device_uri'] if 'width' in device_details: data['width'] = device_details['width'] data['height'] = device_details['height'] @@ -415,7 +415,7 @@ def camera_ui_to_dict(ui): '@name': ui['name'], '@enabled': ui['enabled'], '@proto': ui['proto'], - 'videodevice': ui['device'], + 'videodevice': ui['device_uri'], 'lightswitch': int(ui['light_switch_detect']) * 5, 'auto_brightness': ui['auto_brightness'], 'width': int(ui['resolution'].split('x')[0]), @@ -565,24 +565,15 @@ def camera_ui_to_dict(ui): def camera_dict_to_ui(data): - if data['@proto'] == 'v4l2': - device_uri = data['videodevice'] - usage = utils.get_disk_usage(data['target_dir']) - if usage: - disk_used, disk_total = usage - - else: - disk_used, disk_total = 0, 0 + usage = utils.get_disk_usage(data['target_dir']) + if usage: + disk_used, disk_total = usage else: - device_uri = '%(host)s:%(port)s/config/%(camera_id)s' % { - 'username': data['@username'], - 'password': '***', - 'host': data['@host'], - 'port': data['@port'], - 'camera_id': data['@remote_camera_id']} - - disk_used, disk_total = data['disk_used'], data['disk_total'] + disk_used, disk_total = 0, 0 + + resolutions = v4l2ctl.list_resolutions(data['videodevice']) + resolutions = [(str(w) + 'x' + str(h)) for (w, h) in resolutions] ui = { # device @@ -590,10 +581,13 @@ def camera_dict_to_ui(data): 'enabled': data['@enabled'], 'id': data['@id'], 'proto': data['@proto'], - 'device': device_uri, + 'host': data.get('@host', ''), + 'port': data.get('@port', ''), + 'device_uri': data['videodevice'], 'light_switch_detect': data['lightswitch'] > 0, 'auto_brightness': data['auto_brightness'], 'resolution': str(data['width']) + 'x' + str(data['height']), + 'available_resolutions': resolutions, 'framerate': int(data['framerate']), 'rotation': int(data['rotate']), @@ -662,51 +656,37 @@ def camera_dict_to_ui(data): # the brightness & co. keys in the ui dictionary # indicate the presence of these controls # we must call v4l2ctl functions to determine the available controls - if ui['proto'] == 'v4l2': - brightness = v4l2ctl.get_brightness(ui['device']) - if brightness is not None: # has brightness control - if data.get('brightness', 0) != 0: - ui['brightness'] = brightness - - else: - ui['brightness'] = 50 - - contrast = v4l2ctl.get_contrast(ui['device']) - if contrast is not None: # has contrast control - if data.get('contrast', 0) != 0: - ui['contrast'] = contrast - - else: - ui['contrast'] = 50 - - saturation = v4l2ctl.get_saturation(ui['device']) - if saturation is not None: # has saturation control - if data.get('saturation', 0) != 0: - ui['saturation'] = saturation - - else: - ui['saturation'] = 50 - - hue = v4l2ctl.get_hue(ui['device']) - if hue is not None: # has hue control - if data.get('hue', 0) != 0: - ui['hue'] = hue - - else: - ui['hue'] = 50 - - else: # remote - if 'brightness' in data: - ui['brightness'] = data['brightness'] - - if 'contrast' in data: - ui['contrast'] = data['contrast'] - - if 'saturation' in data: - ui['saturation'] = data['saturation'] - - if 'hue' in data: - ui['hue'] = data['hue'] + brightness = v4l2ctl.get_brightness(ui['device_uri']) + if brightness is not None: # has brightness control + if data.get('brightness', 0) != 0: + ui['brightness'] = brightness + + else: + ui['brightness'] = 50 + + contrast = v4l2ctl.get_contrast(ui['device_uri']) + if contrast is not None: # has contrast control + if data.get('contrast', 0) != 0: + ui['contrast'] = contrast + + else: + ui['contrast'] = 50 + + saturation = v4l2ctl.get_saturation(ui['device_uri']) + if saturation is not None: # has saturation control + if data.get('saturation', 0) != 0: + ui['saturation'] = saturation + + else: + ui['saturation'] = 50 + + hue = v4l2ctl.get_hue(ui['device_uri']) + if hue is not None: # has hue control + if data.get('hue', 0) != 0: + ui['hue'] = hue + + else: + ui['hue'] = 50 text_left = data['text_left'] text_right = data['text_right'] diff --git a/src/handlers.py b/src/handlers.py index 9cd5a4e..81e86de 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -29,6 +29,7 @@ import remote import settings import template import update +import utils import v4l2ctl @@ -162,40 +163,22 @@ class ConfigHandler(BaseHandler): if camera_id not in config.get_camera_ids(): raise HTTPError(404, 'no such camera') - camera_config = config.get_camera(camera_id) - if camera_config['@proto'] != 'v4l2': + local_config = config.get_camera(camera_id) + if local_config['@proto'] != 'v4l2': def on_response(remote_ui_config): - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id')) - - camera_full_url = camera_config['@proto'] + '://' + camera_url - if remote_ui_config is None: return self.finish_json({'error': 'Failed to get remote camera configuration for %(url)s.' % { - 'url': camera_full_url}}) + 'url': utils.make_camera_url(local_config)}}) - for key, value in camera_config.items(): + for key, value in local_config.items(): remote_ui_config[key.replace('@', '')] = value - remote_ui_config['device'] = camera_url - self.finish_json(remote_ui_config) - remote.get_config( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), on_response) + remote.get_config(local_config, on_response) else: - ui_config = config.camera_dict_to_ui(camera_config) - - resolutions = v4l2ctl.list_resolutions(camera_config['videodevice']) - resolutions = [(str(w) + 'x' + str(h)) for (w, h) in resolutions] - ui_config['available_resolutions'] = resolutions + ui_config = config.camera_dict_to_ui(local_config) self.finish_json(ui_config) @@ -237,37 +220,33 @@ class ConfigHandler(BaseHandler): if camera_id not in camera_ids: raise HTTPError(404, 'no such camera') - camera_config = config.get_camera(camera_id) - if camera_config['@proto'] == 'v4l2': - ui_config.setdefault('device', camera_config.get('videodevice', '')) - ui_config.setdefault('proto', camera_config['@proto']) - ui_config.setdefault('enabled', camera_config['@enabled']) + local_config = config.get_camera(camera_id) + if local_config['@proto'] == 'v4l2': +# ui_config.setdefault('device', local_config.get('videodevice', '')) TODO needed? +# ui_config.setdefault('proto', local_config['@proto']) +# ui_config.setdefault('enabled', local_config['@enabled']) - camera_config = config.camera_ui_to_dict(ui_config) - config.set_camera(camera_id, camera_config) + local_config = config.camera_ui_to_dict(ui_config) + config.set_camera(camera_id, local_config) else: # remote camera # update the camera locally - camera_config['@enabled'] = ui_config['enabled'] - config.set_camera(camera_id, camera_config) + local_config['@enabled'] = ui_config['enabled'] + config.set_camera(camera_id, local_config) - # when the camera_config supplied has only the enabled state, + # when the local_config supplied has only the enabled state, # the camera was probably disabled due to errors - if camera_config.has_key('device'): + if ui_config.has_key('device_uri'): # remove the fields that should not get to the remote side - del ui_config['device'] + del ui_config['device_uri'] del ui_config['proto'] + del ui_config['host'] + del ui_config['port'] del ui_config['enabled'] try: - remote.set_config( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), - ui_config) + remote.set_config(local_config, ui_config) except Exception as e: logging.error('failed to set remote camera config: %(msg)s' % {'msg': unicode(e)}) @@ -294,7 +273,7 @@ class ConfigHandler(BaseHandler): reload = True - motionctl.restart() + motionctl.restart() # TODO should not be restarted unless a local camera was changed if not no_finish: self.finish_json() @@ -349,24 +328,13 @@ class ConfigHandler(BaseHandler): else: self.finish_json() - remote.set_preview( - camera_config['@host'], - camera_config['@port'], - camera_config['@username'], - camera_config['@password'], - camera_config['@remote_camera_id'], - controls, on_response) + remote.set_preview(camera_config, controls, on_response) @BaseHandler.auth() def list_cameras(self): logging.debug('listing cameras') - - host = self.get_argument('host', None) - port = self.get_argument('port', None) - username = self.get_argument('username', None) - password = self.get_argument('password', None) - if host: # remote listing + if 'host' in self.get_data(): # remote listing def on_response(cameras): if cameras is None: self.finish_json({'error': 'Failed to list remote cameras.'}) @@ -374,7 +342,7 @@ class ConfigHandler(BaseHandler): else: self.finish_json({'cameras': cameras}) - cameras = remote.list_cameras(host, port, username, password, on_response) + cameras = remote.list_cameras(self.get_data(), on_response) else: # local listing cameras = [] @@ -388,18 +356,12 @@ class ConfigHandler(BaseHandler): cameras.sort(key=lambda c: c['id']) self.finish_json({'cameras': cameras}) - def on_response_builder(camera_id, camera_config): + def on_response_builder(camera_id, local_config): def on_response(remote_ui_config): if remote_ui_config is None: - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id'), - camera_config.get('@proto')) - cameras.append({ 'id': camera_id, - 'name': '<' + camera_url + '>', + 'name': '<' + utils.make_camera_url(local_config) + '>', 'enabled': False, 'streaming_framerate': 1, 'framerate': 1 @@ -407,7 +369,7 @@ class ConfigHandler(BaseHandler): else: remote_ui_config['id'] = camera_id - remote_ui_config['enabled'] = camera_config['@enabled'] # override the enabled status + remote_ui_config['enabled'] = local_config['@enabled'] # override the enabled status cameras.append(remote_ui_config) check_finished() @@ -415,19 +377,14 @@ class ConfigHandler(BaseHandler): return on_response for camera_id in camera_ids: - camera_config = config.get_camera(camera_id) - if camera_config['@proto'] == 'v4l2': - ui_config = config.camera_dict_to_ui(camera_config) + local_config = config.get_camera(camera_id) + if local_config['@proto'] == 'v4l2': + ui_config = config.camera_dict_to_ui(local_config) cameras.append(ui_config) check_finished() else: # remote camera - remote.get_config( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), on_response_builder(camera_id, camera_config)) + remote.get_config(local_config, on_response_builder(camera_id, local_config)) if length[0] == 0: self.finish_json({'cameras': []}) @@ -442,7 +399,7 @@ class ConfigHandler(BaseHandler): if data['@proto'] == 'v4l2': configured_devices[data['videodevice']] = True - devices = [{'device': d[0], 'name': d[1], 'configured': d[0] in configured_devices} + devices = [{'device_uri': d[0], 'name': d[1], 'configured': d[0] in configured_devices} for d in v4l2ctl.list_devices()] self.finish_json({'devices': devices}) @@ -462,7 +419,7 @@ class ConfigHandler(BaseHandler): proto = device_details['proto'] if proto == 'v4l2': # find a suitable resolution - for (w, h) in v4l2ctl.list_resolutions(device_details['device']): + for (w, h) in v4l2ctl.list_resolutions(device_details['device_uri']): if w > 300: device_details['width'] = w device_details['height'] = h @@ -482,9 +439,6 @@ class ConfigHandler(BaseHandler): motionctl.restart() ui_config = config.camera_dict_to_ui(camera_config) - resolutions = v4l2ctl.list_resolutions(camera_config['videodevice']) - resolutions = [(str(w) + 'x' + str(h)) for (w, h) in resolutions] - ui_config['available_resolutions'] = resolutions self.finish_json(ui_config) @@ -492,22 +446,13 @@ class ConfigHandler(BaseHandler): def on_response(remote_ui_config): if remote_ui_config is None: self.finish_json({'error': True}) + + for key, value in camera_config.items(): + remote_ui_config[key.replace('@', '')] = value - tmp_config = config.camera_ui_to_dict(remote_ui_config) - tmp_config.update(camera_config) - tmp_config['disk_used'] = remote_ui_config['disk_used'] - tmp_config['disk_total'] = remote_ui_config['disk_total'] - ui_config = config.camera_dict_to_ui(tmp_config) - ui_config['available_resolutions'] = remote_ui_config['available_resolutions'] + self.finish_json(remote_ui_config) - self.finish_json(ui_config) - - remote.get_config( - device_details.get('host'), - device_details.get('port'), - device_details.get('username'), - device_details.get('password'), - device_details.get('remote_camera_id'), on_response) + remote.get_config(device_details, on_response) @BaseHandler.auth(admin=True) def rem_camera(self, camera_id): @@ -579,15 +524,7 @@ class PictureHandler(BaseHandler): self.finish(picture) - remote.get_current_picture( - camera_config['@host'], - camera_config['@port'], - camera_config['@username'], - camera_config['@password'], - camera_config['@remote_camera_id'], - on_response, - width=width, - height=height) + remote.get_current_picture(camera_config, on_response, width=width, height=height) @BaseHandler.auth() def list(self, camera_id): @@ -599,27 +536,13 @@ class PictureHandler(BaseHandler): camera_config = config.get_camera(camera_id) if camera_config['@proto'] != 'v4l2': def on_response(remote_list): - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id')) - - camera_full_url = camera_config['@proto'] + '://' + camera_url - if remote_list is None: return self.finish_json({'error': 'Failed to get picture list for %(url)s.' % { - 'url': camera_full_url}}) + 'url': utils.make_camera_url(camera_config)}}) self.finish_json(remote_list) - remote.list_media( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), on_response, - media_type='picture', - prefix=self.get_argument('prefix', None)) + remote.list_media(camera_config, on_response, media_type='picture', prefix=self.get_argument('prefix', None)) else: def on_media_list(media_list): @@ -645,16 +568,9 @@ class PictureHandler(BaseHandler): camera_config = config.get_camera(camera_id) if camera_config['@proto'] != 'v4l2': def on_response(response): - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id')) - - camera_full_url = camera_config['@proto'] + '://' + camera_url - if response is None: return self.finish_json({'error': 'Failed to download picture from %(url)s.' % { - 'url': camera_full_url}}) + 'url': utils.make_camera_url(camera_config)}}) pretty_filename = os.path.basename(filename) # no camera name available w/o additional request self.set_header('Content-Type', 'image/jpeg') @@ -662,15 +578,7 @@ class PictureHandler(BaseHandler): self.finish(response) - remote.get_media_content( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), - on_response, - filename=filename, - media_type='picture') + remote.get_media_content(camera_config, on_response, filename=filename, media_type='picture') else: content = mediafiles.get_media_content(camera_config, filename, 'picture') @@ -702,15 +610,7 @@ class PictureHandler(BaseHandler): self.finish(content) - remote.get_media_preview( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), - on_response, - filename=filename, - media_type='picture', + remote.get_media_preview(camera_config, on_response, filename=filename, media_type='picture', width=self.get_argument('width', None), height=self.get_argument('height', None)) @@ -759,27 +659,13 @@ class MovieHandler(BaseHandler): camera_config = config.get_camera(camera_id) if camera_config['@proto'] != 'v4l2': def on_response(remote_list): - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id')) - - camera_full_url = camera_config['@proto'] + '://' + camera_url - if remote_list is None: return self.finish_json({'error': 'Failed to get movie list for %(url)s.' % { - 'url': camera_full_url}}) + 'url': utils.make_camera_url(camera_config)}}) self.finish_json(remote_list) - remote.list_media( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), on_response, - media_type='movie', - prefix=self.get_argument('prefix', None)) + remote.list_media(camera_config, on_response, media_type='movie', prefix=self.get_argument('prefix', None)) else: def on_media_list(media_list): @@ -805,16 +691,9 @@ class MovieHandler(BaseHandler): camera_config = config.get_camera(camera_id) if camera_config['@proto'] != 'v4l2': def on_response(response): - camera_url = remote.make_remote_camera_url( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@remote_camera_id')) - - camera_full_url = camera_config['@proto'] + '://' + camera_url - if response is None: return self.finish_json({'error': 'Failed to download movie from %(url)s.' % { - 'url': camera_full_url}}) + 'url': utils.make_camera_url(camera_config)}}) pretty_filename = os.path.basename(filename) # no camera name available w/o additional request self.set_header('Content-Type', 'video/mpeg') @@ -822,15 +701,7 @@ class MovieHandler(BaseHandler): self.finish(response) - remote.get_media_content( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), - on_response, - filename=filename, - media_type='movie') + remote.get_media_content(camera_config, on_response, filename=filename, media_type='movie') else: content = mediafiles.get_media_content(camera_config, filename, 'movie') @@ -861,15 +732,7 @@ class MovieHandler(BaseHandler): self.finish(content) - remote.get_media_preview( - camera_config.get('@host'), - camera_config.get('@port'), - camera_config.get('@username'), - camera_config.get('@password'), - camera_config.get('@remote_camera_id'), - on_response, - filename=filename, - media_type='movie', + remote.get_media_preview(camera_config, on_response, filename=filename, media_type='movie', width=self.get_argument('width', None), height=self.get_argument('height', None)) diff --git a/src/remote.py b/src/remote.py index 856326e..cbcde73 100644 --- a/src/remote.py +++ b/src/remote.py @@ -39,19 +39,16 @@ def _make_request(host, port, username, password, uri, method='GET', data=None, return request -def make_remote_camera_url(host, port, camera_id, proto=''): - if proto: - proto += '://' - - return '%(proto)s%(host)s:%(port)s/config/%(camera_id)s' % { - 'host': host, - 'port': port, - 'camera_id': camera_id, - 'proto': proto - } +def make_camera_uri(camera_id): + return '/config/%(camera_id)s' % {'camera_id': camera_id} -def list_cameras(host, port, username, password, callback): +def list_cameras(local_config, callback): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + logging.debug('listing remote cameras on %(host)s:%(port)s' % { 'host': host, 'port': port}) @@ -84,7 +81,13 @@ def list_cameras(host, port, username, password, callback): http_client.fetch(request, on_response) -def get_config(host, port, username, password, camera_id, callback): +def get_config(local_config, callback): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('getting config for remote camera %(id)s on %(host)s:%(port)s' % { 'id': camera_id, 'host': host, @@ -112,6 +115,10 @@ def get_config(host, port, username, password, camera_id, callback): 'msg': unicode(e)}) return callback(None) + + response['host'] = host + response['port'] = port + response['device_uri'] = make_camera_uri(camera_id) callback(response) @@ -119,15 +126,21 @@ def get_config(host, port, username, password, camera_id, callback): http_client.fetch(request, on_response) -def set_config(host, port, username, password, camera_id, data): +def set_config(local_config, camera_config): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('setting config for remote camera %(id)s on %(host)s:%(port)s' % { 'id': camera_id, 'host': host, 'port': port}) - data = json.dumps(data) + camera_config = json.dumps(camera_config) - request = _make_request(host, port, username, password, '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=data) + request = _make_request(host, port, username, password, '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=camera_config) try: http_client = HTTPClient() @@ -145,7 +158,13 @@ def set_config(host, port, username, password, camera_id, data): raise -def set_preview(host, port, username, password, camera_id, controls, callback): +def set_preview(local_config, controls, callback): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('setting preview for remote camera %(id)s on %(host)s:%(port)s' % { 'id': camera_id, 'host': host, @@ -171,7 +190,13 @@ def set_preview(host, port, username, password, camera_id, controls, callback): http_client.fetch(request, on_response) -def get_current_picture(host, port, username, password, camera_id, callback, width, height): +def get_current_picture(local_config, callback, width, height): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('getting current picture for remote camera %(id)s on %(host)s:%(port)s' % { 'id': camera_id, 'host': host, @@ -203,7 +228,13 @@ def get_current_picture(host, port, username, password, camera_id, callback, wid http_client.fetch(request, on_response) -def list_media(host, port, username, password, camera_id, callback, media_type, prefix=None): +def list_media(local_config, callback, media_type, prefix=None): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('getting media list for remote camera %(id)s on %(host)s:%(port)s' % { 'id': camera_id, 'host': host, @@ -243,7 +274,13 @@ def list_media(host, port, username, password, camera_id, callback, media_type, http_client.fetch(request, on_response) -def get_media_content(host, port, username, password, camera_id, callback, filename, media_type): +def get_media_content(local_config, callback, filename, media_type): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('downloading file %(filename)s of remote camera %(id)s on %(host)s:%(port)s' % { 'filename': filename, 'id': camera_id, @@ -274,7 +311,13 @@ def get_media_content(host, port, username, password, camera_id, callback, filen http_client.fetch(request, on_response) -def get_media_preview(host, port, username, password, camera_id, callback, filename, media_type, width, height): +def get_media_preview(local_config, callback, filename, media_type, width, height): + host = local_config.get('@host', local_config.get('host')) + port = local_config.get('@port', local_config.get('port')) + username = local_config.get('@username', local_config.get('username')) + password = local_config.get('@password', local_config.get('password')) + camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id')) + logging.debug('getting file preview for %(filename)s of remote camera %(id)s on %(host)s:%(port)s' % { 'filename': filename, 'id': camera_id, diff --git a/src/utils.py b/src/utils.py index d6fff8a..3064596 100644 --- a/src/utils.py +++ b/src/utils.py @@ -19,6 +19,8 @@ import datetime import logging import os +import remote + def pretty_date_time(date_time, tzinfo=None): if date_time is None: @@ -206,3 +208,12 @@ def get_disk_usage(path): used_size = total_size - free_size return (used_size, total_size) + + +def make_camera_url(config): + proto = config.get('proto', config.get('@proto', '')) + host = config.get('host', config.get('@host', '')) + port = config.get('port', config.get('@port', '')) + device_uri = config.get('device_uri', config.get('videodevice', remote.make_camera_uri(config.get('@remote_camera_id')))) + + return proto + '://' + host + (str(port) + ':' if port else '') + device_uri diff --git a/static/js/main.js b/static/js/main.js index d8abd03..a804309 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -251,7 +251,7 @@ function initUI() { updateConfigUi(); } else { - showProgress(); + beginProgress(); fetchCurrentCameraConfig(endProgress); } }); @@ -508,12 +508,24 @@ function cameraUi2Dict() { }; } + var deviceUrl = $('#deviceEntry').val(); + var parts = deviceUrl.split('://'); + var proto = parts[0]; + parts = parts[1].split('/'); + var hostPort = parts[0]; + var deviceUri = '/' + parts.slice(1).join('/'); + parts = hostPort.split(':'); + var host = parts[0]; + var port = parts[1] || ''; + var dict = { /* video device */ 'enabled': $('#videoDeviceSwitch')[0].checked, 'name': $('#deviceNameEntry').val(), - 'proto': $('#deviceEntry').val().split('://')[0], - 'device': $('#deviceEntry').val().split('://')[1], + 'proto': proto, + 'host': host, + 'port': port, + 'device_uri': deviceUri, 'light_switch_detect': $('#lightSwitchDetectSwitch')[0].checked, 'auto_brightness': $('#autoBrightnessSwitch')[0].checked, 'resolution': $('#resolutionSelect').val(), @@ -624,7 +636,7 @@ function dict2CameraUi(dict) { /* video device */ $('#videoDeviceSwitch')[0].checked = dict['enabled']; $('#deviceNameEntry').val(dict['name']); - $('#deviceEntry').val(dict['proto'] + '://' + dict['device']); + $('#deviceEntry').val(dict['proto'] + '://' + dict['host'] + (dict['port'] ? ':' + dict['port'] : '') + dict['device_uri']); $('#lightSwitchDetectSwitch')[0].checked = dict['light_switch_detect']; $('#autoBrightnessSwitch')[0].checked = dict['auto_brightness']; @@ -722,26 +734,14 @@ function dict2CameraUi(dict) { } - /* apply button */ - -function showApply() { - var applyButton = $('#applyButton'); - - applyButton.html('Apply'); - applyButton.css('display', 'inline-block'); - applyButton.removeClass('progress'); - setTimeout(function () { - applyButton.css('opacity', '1'); - }, 10); -} + /* progress */ -function showProgress() { +function beginProgress(cameraIds) { if (inProgress) { return; /* already in progress */ } inProgress = true; - refreshDisabled++; /* replace the main page message with a progress indicator */ $('div.add-camera-message').html(''); @@ -750,30 +750,25 @@ function showProgress() { $('#applyButton').html(''); /* show the camera progress indicators */ - $('div.camera-progress').css('opacity', '0.5'); + if (cameraIds) { + cameraIds.forEach(function (cameraId) { + $('div.camera-frame#' + cameraId + ' div.camera-progress').css('opacity', '0.5'); + }); + } + else { + $('div.camera-progress').css('opacity', '0.5'); + } /* remove the settings progress lock */ $('div.settings-progress').css('width', '100%').css('opacity', '0.9'); } -function hideApply() { - var applyButton = $('#applyButton'); - - applyButton.css('opacity', '0'); - applyButton.removeClass('progress'); - - setTimeout(function () { - applyButton.css('display', 'none'); - }, 500); -} - function endProgress() { if (!inProgress) { return; /* not in progress */ } inProgress = false; - refreshDisabled--; /* remove any existing message on the main page */ $('div.add-camera-message').remove(); @@ -797,6 +792,31 @@ function endProgress() { }, 500); } + + /* apply button */ + +function showApply() { + var applyButton = $('#applyButton'); + + applyButton.html('Apply'); + applyButton.css('display', 'inline-block'); + applyButton.removeClass('progress'); + setTimeout(function () { + applyButton.css('opacity', '1'); + }, 10); +} + +function hideApply() { + var applyButton = $('#applyButton'); + + applyButton.css('opacity', '0'); + applyButton.removeClass('progress'); + + setTimeout(function () { + applyButton.css('display', 'none'); + }, 500); +} + function isApplyVisible() { var applyButton = $('#applyButton'); @@ -810,7 +830,7 @@ function doApply() { return; } - showProgress(); + beginProgress(); ajax('POST', '/config/0/set/', pushConfigs, function (data) { if (data == null || data.error) { @@ -857,7 +877,7 @@ function doRemCamera() { var deviceName = $('#videoDeviceSelect').find('option[value=' + cameraId + ']').text(); runConfirmDialog('Remove camera ' + deviceName + '?', function () { - showProgress(); + beginProgress(); ajax('POST', '/config/' + cameraId + '/rem/', null, function (data) { if (data == null || data.error) { endProgress(); @@ -1057,6 +1077,10 @@ function pushPreview(control) { }); } +function getCameraIdsByInstance() { + +} + /* dialogs */ @@ -1312,7 +1336,7 @@ function runAddCameraDialog() { /* add available devices */ data.devices.forEach(function (device) { if (!device.configured) { - deviceSelect.append(''); + deviceSelect.append(''); } }); @@ -1341,10 +1365,10 @@ function runAddCameraDialog() { } else { data.proto = 'v4l2'; - data.device = deviceSelect.val(); + data.device_uri = deviceSelect.val(); } - showProgress(); + beginProgress(); ajax('POST', '/config/add/', data, function (data) { if (data == null || data.error) { endProgress(); @@ -1714,7 +1738,7 @@ function addCameraFrameUi(cameraId, cameraName, framerate) { /* error and load handlers */ cameraImg.error(function () { this.error = true; - this.loading = false; + this.loading = 0; cameraImg.addClass('error').removeClass('loading'); cameraImg.height(Math.round(cameraImg.width() * 0.75)); @@ -1727,7 +1751,7 @@ function addCameraFrameUi(cameraId, cameraName, framerate) { } this.error = false; - this.loading = false; + this.loading = 0; cameraImg.removeClass('error').removeClass('loading'); cameraImg.css('height', ''); @@ -1864,7 +1888,14 @@ function refreshCameraFrames() { function refreshCameraFrame(cameraId, img, fast) { if (img.loading) { - return; /* still loading the previous image */ + img.loading++; /* increases each time the camera would refresh but is still loading */ + + if (img.loading > 5) { + img.loading = 0; + } + else { + return; /* wait for the previous frame to finish loading */ + } } var timestamp = new Date().getTime(); @@ -1873,7 +1904,7 @@ function refreshCameraFrames() { } timestamp = Math.round(timestamp); img.src = '/picture/' + cameraId + '/current/?seq=' + timestamp + '&width=' + img.width; - img.loading = true; + img.loading = 1; } var cameraFrames; @@ -1951,7 +1982,7 @@ $(document).ready(function () { }); initUI(); - showProgress(); + beginProgress(); fetchCurrentConfig(endProgress); refreshCameraFrames(); checkCameraErrors(); -- 2.39.5