]> www.vanbest.org Git - motioneye-debian/commitdiff
added a configuration mechanism
authorCalin Crisan <ccrisan@gmail.com>
Sat, 21 Sep 2013 13:00:49 +0000 (16:00 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sat, 21 Sep 2013 13:53:05 +0000 (16:53 +0300)
.gitignore
conf/.gitkeep [new file with mode: 0644]
doc/todo.txt
src/config.py [new file with mode: 0644]
src/handlers.py
src/server.py
temp/.gitkeep [deleted file]

index d7c6380aa57366ef004ec89857a6c67ca3ccd380..6abc30f46653e735715f8382699db6f4a76dfd23 100644 (file)
@@ -3,3 +3,5 @@
 *.bak
 .project
 .pydevproject
+*.conf
+*.json
diff --git a/conf/.gitkeep b/conf/.gitkeep
new file mode 100644 (file)
index 0000000..0538fcf
--- /dev/null
@@ -0,0 +1 @@
+this directory contains config files for motionEye as well as for motion
\ No newline at end of file
index ee68a787baba7e63435e0ef0e485484e830db3b4..9cc7c3eb9a9b15525921ee7b9c1d3458960eef9c 100644 (file)
@@ -1,3 +1,5 @@
+-> config.py functions should make use of exceptions rather than returning None
+
 -> browser compatibility test
 -> authentication
 -> proxy for slave motioneyes
diff --git a/src/config.py b/src/config.py
new file mode 100644 (file)
index 0000000..a268f1c
--- /dev/null
@@ -0,0 +1,435 @@
+
+import errno
+import json
+import logging
+import os.path
+import re
+
+import settings
+
+
+_CONFIG_DIR = 'conf'
+_CAMERA_CONFIG_FILE_NAME = 'thread-%(id)s.conf'
+
+_GENERAL_CONFIG_FILE_PATH = os.path.join(_CONFIG_DIR, 'motion-eye.json')
+_MOTION_CONFIG_FILE_PATH = os.path.join(_CONFIG_DIR, 'motion.conf')
+_CAMERA_CONFIG_FILE_PATH = os.path.join(_CONFIG_DIR, _CAMERA_CONFIG_FILE_NAME)
+
+
+def get_general():
+    config_file_path = os.path.join(settings.PROJECT_PATH, _GENERAL_CONFIG_FILE_PATH)
+    
+    logging.info('reading general config from file %(path)s...' % {'path': config_file_path})
+    
+    try:
+        file = open(config_file_path, 'r')
+    
+    except IOError as e:
+        if e.errno == errno.ENOENT:  # file does not exist
+            logging.info('config file %(path)s does not exist, creating a new default one...' % {'path': config_file_path})
+            
+            return set_general({})
+        
+        else:
+            logging.error('could not open config file %(path)s: %(msg)s' % {
+                    'path': config_file_path, 'msg': unicode(e)})
+            
+            return None
+    
+    try:
+        data = json.load(file)
+        _set_default_general(data)
+        
+        return data
+    
+    except Exception as e:
+        logging.error('could not read config file %(path)s: %(msg)s' % {
+                'path': config_file_path, 'msg': unicode(e)})
+        
+    finally:
+        file.close()
+        
+    return None
+
+
+def set_general(data):
+    _set_default_general(data)
+
+    config_file_path = os.path.join(settings.PROJECT_PATH, _GENERAL_CONFIG_FILE_PATH)
+    
+    logging.info('writing general config to file %(path)s...' % {'path': config_file_path})
+    
+    try:
+        file = open(config_file_path, 'w')
+    
+    except Exception as e:
+        logging.error('could not open config file %(path)s for writing: %(msg)s' % {
+                'path': config_file_path, 'msg': unicode(e)})
+        
+        return None
+    
+    try:
+        json.dump(file, data)
+    
+    except Exception as e:
+        logging.error('could not write config file %(path)s: %(msg)s' % {
+                'path': config_file_path, 'msg': unicode(e)})
+        
+    finally:
+        file.close()
+        
+    return data
+
+
+def get_cameras():
+    config_path = os.path.join(settings.PROJECT_PATH, _CONFIG_DIR)
+    
+    logging.info('loading cameras from directory %(path)s...' % {'path': config_path})
+    
+    try:
+        ls = os.listdir(config_path)
+        
+    except Exception as e:
+        logging.error('could not list contents of %(dir)s: %(msg)s' % {
+                'dir': config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    cameras = {}
+    
+    pattern = _CAMERA_CONFIG_FILE_NAME.replace('%(id)s', '(\w+)')
+    for name in ls:
+        match = re.match(pattern, name)
+        if not match:
+            continue # not a camera config file
+        
+        camera_id = match.groups()[0]
+        
+        camera_config_path = os.path.join(config_path, name)
+        
+        logging.info('reading camera config from %(path)s...' % {'path': camera_config_path})
+        
+        try:
+            file = open(camera_config_path, 'r')
+        
+        except Exception as e:
+            logging.error('could not open camera config file %(path)s: %(msg)s' % {
+                    'path': camera_config_path, 'msg': unicode(e)})
+            
+            continue
+        
+        try:
+            lines = [l[:-1] for l in file.readlines()]
+        
+        except Exception as e:
+            logging.error('could not read camera config file %(path)s: %(msg)s' % {
+                    'path': camera_config_path, 'msg': unicode(e)})
+            
+            continue
+        
+        finally:
+            file.close()
+        
+        data = _conf_to_dict(lines)
+        _set_default_motion_camera(data)
+        
+        cameras[camera_id] = data
+        
+    logging.info('loaded %(count)d cameras' % {'count': len(cameras)})
+    
+    return cameras
+        
+
+def get_camera(camera_id):
+    config_path = os.path.join(settings.PROJECT_PATH, _CONFIG_DIR)
+    camera_config_path = os.path.join(config_path, _CAMERA_CONFIG_FILE_NAME % {'id': camera_id})
+    
+    logging.info('reading camera config from %(path)s...' % {'path': camera_config_path})
+    
+    try:
+        file = open(camera_config_path, 'r')
+    
+    except Exception as e:
+        logging.error('could not open camera config file: %(msg)s' % {'msg': unicode(e)})
+        
+        return None
+    
+    try:
+        lines = [l[:-1] for l in file.readlines()]
+    
+    except Exception as e:
+        logging.error('could not read camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    finally:
+        file.close()
+    
+    data = _conf_to_dict(lines)
+    _set_default_motion_camera(data)
+    
+    return data
+
+
+def set_camera(camera_id, data):
+    config_path = os.path.join(settings.PROJECT_PATH, _CONFIG_DIR)
+    camera_config_path = os.path.join(config_path, _CAMERA_CONFIG_FILE_NAME % {'id': camera_id})
+    
+    # read the actual configuration from file
+    
+    logging.info('reading camera config from %(path)s...' % {'path': camera_config_path})
+    
+    try:
+        file = open(camera_config_path, 'r')
+    
+    except Exception as e:
+        logging.error('could not open camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    try:
+        lines = [l[:-1] for l in file.readlines()]
+    
+    except Exception as e:
+        logging.error('could not read camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    finally:
+        file.close()
+    
+    # write the configuration to file
+    
+    logging.info('writing camera config to %(path)s...' % {'path': camera_config_path})
+    
+    try:
+        file = open(camera_config_path, 'w')
+    
+    except Exception as e:
+        logging.error('could not open camera config file %(path)s for writing: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    lines = _dict_to_conf(lines, data)
+    
+    try:
+        file.writelines([l + '\n' for l in lines])
+    
+    except Exception as e:
+        logging.error('could not write camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    finally:
+        file.close()
+    
+    return data
+
+
+def add_camera():
+    config_path = os.path.join(settings.PROJECT_PATH, _CONFIG_DIR)
+    
+    logging.info('loading cameras from directory %(path)s...' % {'path': config_path})
+    
+    try:
+        ls = os.listdir(config_path)
+        
+    except Exception as e:
+        logging.error('could not list contents of %(dir)s: %(msg)s' % {
+                'dir': config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    camera_ids = []
+    
+    pattern = _CAMERA_CONFIG_FILE_NAME.replace('%(id)s', '(\w+)')
+    for name in ls:
+        match = re.match(pattern, name)
+        if not match:
+            continue # not a camera config file
+        
+        camera_id = match.groups()[0]
+        try:
+            camera_id = int(camera_id)
+        
+        except ValueError:
+            logging.error('camera id is not an integer: %(id)s' % {'id': camera_id})
+            
+            continue
+            
+        camera_ids.append(camera_id)
+        
+        logging.debug('found camera with id %(id)s' % {'id': camera_id})
+    
+    last_camera_id = max(camera_ids or [0])
+    camera_id = last_camera_id + 1
+    
+    logging.info('adding new camera with id %(id)s...' % {'id': camera_id})
+        
+    # write the configuration to file
+    
+    camera_config_path = os.path.join(config_path, _CAMERA_CONFIG_FILE_NAME % {'id': camera_id})
+    logging.info('writing camera config to %(path)s...' % {'path': camera_config_path})
+    
+    try:
+        file = open(camera_config_path, 'w')
+    
+    except Exception as e:
+        logging.error('could not open camera config file %(path)s for writing: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    data = {}
+    _set_default_motion_camera(data)
+    
+    lines = _dict_to_conf([], data)
+    
+    try:
+        file.writelines([l + '\n' for l in lines])
+    
+    except Exception as e:
+        logging.error('could not write camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+    finally:
+        file.close()
+    
+    return camera_id, data
+
+
+def rem_camera(camera_id):
+    config_path = os.path.join(settings.PROJECT_PATH, _CONFIG_DIR)
+    camera_config_path = os.path.join(config_path, _CAMERA_CONFIG_FILE_NAME % {'id': camera_id})
+    
+    logging.info('removing camera config file %(path)s...' % {'path': camera_config_path})
+    
+    try:
+        os.remove(camera_config_path)
+    
+    except Exception as e:
+        logging.error('could not remove camera config file %(path)s: %(msg)s' % {
+                'path': camera_config_path, 'msg': unicode(e)})
+        
+        return None
+    
+
+def _value_to_python(value):
+    value_lower = value.lower()
+    if value_lower == 'off':
+        return False
+    
+    elif value_lower == 'on':
+        return True
+    
+    try:
+        return int(value)
+    
+    except ValueError:
+        try:
+            return float(value)
+        
+        except ValueError:
+            return value
+
+
+def _python_to_value(value):
+    if value is True:
+        return 'on'
+    
+    elif value is False:
+        return 'off'
+    
+    elif isinstance(value, (int, float)):
+        return str(value)
+    
+    else:
+        return value
+
+
+def _conf_to_dict(lines):
+    data = {}
+    
+    for line in lines:
+        line = line.strip()
+        if len(line) == 0:  # empty line
+            continue
+        
+        if line.startswith('#') or line.startswith(';'):  # comment line
+            continue
+        
+        parts = line.split(None, 1)
+        if len(parts) != 2:  # invalid line format
+            continue
+        
+        (name, value) = parts
+        value = value.strip()
+        
+        value = data[name] = _value_to_python(value)
+    
+    return data
+
+
+def _dict_to_conf(lines, data):
+    conf_lines = []
+    data_copy = dict(data)
+    
+    # parse existing lines and replace the values
+    
+    for line in lines:
+        line = line.strip()
+        if len(line) == 0:  # empty line
+            conf_lines.append(line)
+            continue
+        
+        if line.startswith('#') or line.startswith(';'):  # comment line
+            conf_lines.append(line)
+            continue
+        
+        parts = line.split(None, 1)
+        if len(parts) != 2:  # invalid line format
+            conf_lines.append(line)
+            continue
+        
+        (name, value) = parts
+        
+        new_value = data.get(name)
+        if new_value is not None:
+            value = _python_to_value(new_value)
+            
+        line = name + ' ' + value
+        conf_lines.append(line)
+        
+        del data_copy[name]
+    
+    # add the remaining config values not covered by existing lines
+    
+    for (name, value) in data_copy:
+        line = name + ' ' + value
+        conf_lines.append(line)
+        
+    return conf_lines
+
+
+def _set_default_general(data):
+    data.set_default('show_advanced', False)
+    data.set_default('admin_username', 'admin')
+    data.set_default('admin_password', '')
+    data.set_default('normal_username', 'user')
+    data.set_default('storage_device', 'local-disk')
+    data.set_default('root_directory', '/')
+
+
+def _set_default_motion(data):
+    pass
+
+
+def _set_default_motion_camera(data):
+    pass
index cd78db673bcc9b9894653f19d0fd879a4c4ce989..dedd2c7bf72d6121379b2ea35d606b45f6304029 100644 (file)
@@ -46,10 +46,18 @@ class ConfigHandler(BaseHandler):
             raise HTTPError(400, 'unknown operation')
     
     def get_config(self, camera_id):
-        logging.debug('getting config for camera %(id)s' % {'camera': camera_id})
+        if camera_id:
+            logging.debug('getting config for camera %(id)s' % {'camera': camera_id})
+            
+        else:
+            logging.debug('getting general config')
     
     def set_config(self, camera_id):
-        logging.debug('setting config for camera %(id)s' % {'camera': camera_id})
+        if camera_id:
+            logging.debug('setting config for camera %(id)s' % {'camera': camera_id})
+            
+        else:
+            logging.debug('setting general config')
     
     def add_camera(self):
         logging.debug('adding new camera')
index 8d7b86bf8d24b380d08f0a1e15fbb99731fe2dc5..19c228be9022c2d83cf25f802e4fe915fbd954f3 100644 (file)
@@ -11,6 +11,7 @@ application = Application(
         (r'^/$', handlers.MainHandler),
         (r'^/config/(?P<camera_id>\w+)/(?P<op>get|set|rem)/?$', handlers.ConfigHandler),
         (r'^/config/(?P<op>add)/?$', handlers.ConfigHandler),
+        (r'^/config/general/(?P<op>set|get)/?$', handlers.ConfigHandler),
         (r'^/snapshot/(?P<camera_id>\w+)/(?P<op>current|list)/?$', handlers.SnapshotHandler),
         (r'^/snapshot/(?P<camera_id>\w+)/(?P<op>download)/(?P<filename>.+)/?$', handlers.SnapshotHandler),
         (r'^/movie/(?P<camera_id>\w+)/(?P<op>list)/?$', handlers.MovieHandler),
diff --git a/temp/.gitkeep b/temp/.gitkeep
deleted file mode 100644 (file)
index 18d93d2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-this directory contains (at runtime) temporary config files for motion
\ No newline at end of file