From: Calin Crisan Date: Sat, 5 Aug 2017 09:55:52 +0000 (+0300) Subject: a few PEP8 code cleanups X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=f5175d8a8ae7b0cf1974ca9205a3d747166bdd03;p=motioneye-debian a few PEP8 code cleanups --- diff --git a/.gitignore b/.gitignore index a41af75..ebdc131 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .pydevproject *.conf .settings +.idea run dist motioneye.egg-info diff --git a/motioneye/cleanup.py b/motioneye/cleanup.py index 6f122d3..9e4aa46 100644 --- a/motioneye/cleanup.py +++ b/motioneye/cleanup.py @@ -68,7 +68,7 @@ def _run_process(): # schedule the next call io_loop.add_timeout(datetime.timedelta(seconds=settings.CLEANUP_INTERVAL), _run_process) - if not running(): # check that the previous process has finished + if not running(): # check that the previous process has finished logging.debug('running cleanup process...') _process = multiprocessing.Process(target=_do_cleanup) diff --git a/motioneye/config.py b/motioneye/config.py index 48e8cf5..07a8432 100644 --- a/motioneye/config.py +++ b/motioneye/config.py @@ -41,8 +41,9 @@ import v4l2ctl _CAMERA_CONFIG_FILE_NAME = 'thread-%(id)s.conf' _MAIN_CONFIG_FILE_NAME = 'motion.conf' -_ACTIONS = ['lock', 'unlock', 'light_on', 'light_off', 'alarm_on', 'alarm_off', 'up', 'right', 'down', 'left', 'zoom_in', 'zoom_out', - 'preset1', 'preset2', 'preset3', 'preset4', 'preset5', 'preset6', 'preset7', 'preset8', 'preset9'] +_ACTIONS = ['lock', 'unlock', 'light_on', 'light_off', 'alarm_on', 'alarm_off', 'up', + 'right', 'down', 'left', 'zoom_in', 'zoom_out', + 'preset1', 'preset2', 'preset3', 'preset4', 'preset5', 'preset6', 'preset7', 'preset8', 'preset9'] _main_config_cache = None _camera_config_cache = {} @@ -54,11 +55,11 @@ _monitor_command_cache = {} # when using the following video codecs, the ffmpeg_variable_bitrate parameter appears to have an exponential effect _EXPONENTIAL_QUALITY_CODECS = ['mpeg4', 'msmpeg4', 'swf', 'flv', 'mov', 'mkv'] -_EXPONENTIAL_QUALITY_FACTOR = 100000 # voodoo -_EXPONENTIAL_DEF_QUALITY = 511 # about 75% +_EXPONENTIAL_QUALITY_FACTOR = 100000 # voodoo +_EXPONENTIAL_DEF_QUALITY = 511 # about 75% _MAX_FFMPEG_VARIABLE_BITRATE = 32767 -_KNOWN_MOTION_OPTIONS = set([ +_KNOWN_MOTION_OPTIONS = { 'auto_brightness', 'brightness', 'contrast', @@ -117,8 +118,8 @@ _KNOWN_MOTION_OPTIONS = set([ 'text_right', 'threshold', 'videodevice', - 'width', -]) + 'width' +} def additional_section(func): @@ -145,13 +146,14 @@ def get_main(as_lines=False): lines = None try: - file = open(config_file_path, 'r') + f = open(config_file_path, 'r') except IOError as e: if e.errno == errno.ENOENT: # file does not exist logging.info('main config file %(path)s does not exist, using default values' % {'path': config_file_path}) lines = [] + f = None else: logging.error('could not open main config file %(path)s: %(msg)s' % { @@ -159,9 +161,9 @@ def get_main(as_lines=False): raise - if lines is None: + if lines is None and f: try: - lines = [l[:-1] for l in file.readlines()] + lines = [l[:-1] for l in f.readlines()] except Exception as e: logging.error('could not read main config file %(path)s: %(msg)s' % { @@ -170,7 +172,7 @@ def get_main(as_lines=False): raise finally: - file.close() + f.close() if as_lines: return lines @@ -207,7 +209,7 @@ def set_main(main_config): logging.debug('writing main config to %(path)s...' % {'path': config_file_path}) try: - file = open(config_file_path, 'w') + f = open(config_file_path, 'w') except Exception as e: logging.error('could not open main config file %(path)s for writing: %(msg)s' % { @@ -218,7 +220,7 @@ def set_main(main_config): lines = _dict_to_conf(lines, main_config, list_names=['thread']) try: - file.writelines([utils.make_str(l) + '\n' for l in lines]) + f.writelines([utils.make_str(l) + '\n' for l in lines]) except Exception as e: logging.error('could not write main config file %(path)s: %(msg)s' % { @@ -227,7 +229,7 @@ def set_main(main_config): raise finally: - file.close() + f.close() def get_camera_ids(filter_valid=True): @@ -316,7 +318,7 @@ def get_camera(camera_id, as_lines=False): logging.debug('reading camera config from %(path)s...' % {'path': camera_config_path}) try: - file = open(camera_config_path, 'r') + f = open(camera_config_path, 'r') except Exception as e: logging.error('could not open camera config file: %(msg)s' % {'msg': unicode(e)}) @@ -324,7 +326,7 @@ def get_camera(camera_id, as_lines=False): raise try: - lines = [l.strip() for l in file.readlines()] + lines = [l.strip() for l in f.readlines()] except Exception as e: logging.error('could not read camera config file %(path)s: %(msg)s' % { @@ -333,7 +335,7 @@ def get_camera(camera_id, as_lines=False): raise finally: - file.close() + f.close() if as_lines: return lines @@ -399,7 +401,7 @@ def get_camera(camera_id, as_lines=False): _set_default_simple_mjpeg_camera(camera_id, camera_config) - else: # incomplete configuration + else: # incomplete configuration logging.warn('camera config file at %s is incomplete, ignoring' % camera_config_path) return None @@ -491,7 +493,7 @@ def set_camera(camera_id, camera_config): logging.debug('writing camera config to %(path)s...' % {'path': camera_config_path}) try: - file = open(camera_config_path, 'w') + f = open(camera_config_path, 'w') except Exception as e: logging.error('could not open camera config file %(path)s for writing: %(msg)s' % { @@ -502,7 +504,7 @@ def set_camera(camera_id, camera_config): lines = _dict_to_conf(lines, camera_config) try: - file.writelines([utils.make_str(l) + '\n' for l in lines]) + f.writelines([utils.make_str(l) + '\n' for l in lines]) except Exception as e: logging.error('could not write camera config file %(path)s: %(msg)s' % { @@ -511,7 +513,7 @@ def set_camera(camera_id, camera_config): raise finally: - file.close() + f.close() def add_camera(device_details): @@ -530,7 +532,8 @@ def add_camera(device_details): else: host = device_details['username'] + '@' + host - device_details['url'] = urlparse.urlunparse((device_details['scheme'], host, device_details['path'], '', '', '')) + device_details['url'] = urlparse.urlunparse( + (device_details['scheme'], host, device_details['path'], '', '', '')) # determine the last camera id camera_ids = get_camera_ids() @@ -580,7 +583,7 @@ def add_camera(device_details): camera_config['width'] = 640 camera_config['height'] = 480 - else: # assuming mjpeg + else: # assuming mjpeg camera_config['@proto'] = 'mjpeg' camera_config['@url'] = device_details['url'] @@ -680,7 +683,7 @@ def motion_camera_ui_to_dict(ui, old_config=None): import smbctl old_config = dict(old_config or {}) - main_config = get_main() # needed for surveillance password + main_config = get_main() # needed for surveillance password data = { # device @@ -813,7 +816,7 @@ def motion_camera_ui_to_dict(ui, old_config=None): else: data['hue'] = max(1, int(round(int(ui['hue']) * 2.55))) - else: # assuming netcam + else: # assuming netcam if data.get('netcam_url', old_config.get('netcam_url', '')).startswith('rtsp'): # motion uses the configured width and height for RTSP cameras width = int(ui['resolution'].split('x')[0]) @@ -823,7 +826,7 @@ def motion_camera_ui_to_dict(ui, old_config=None): threshold = int(float(ui['frame_change_threshold']) * width * height / 100) - else: # width & height are not available for other netcams + else: # width & height are not available for other netcams threshold = int(float(ui['frame_change_threshold']) * 640 * 480 / 100) data['threshold'] = threshold @@ -852,9 +855,9 @@ def motion_camera_ui_to_dict(ui, old_config=None): os.makedirs(data['target_dir']) logging.debug('created root directory %s for camera %s' % (data['target_dir'], data['@name'])) - except Exception as e: + except OSError as e: if isinstance(e, OSError) and e.errno == errno.EEXIST: - pass # already exists, things should be just fine + pass # already exists, things should be just fine else: logging.error('failed to create root directory "%s": %s' % (data['target_dir'], e), exc_info=True) @@ -923,7 +926,8 @@ def motion_camera_ui_to_dict(ui, old_config=None): q = int(ui['movie_quality']) if motionctl.needs_ffvb_quirks(): if data['ffmpeg_video_codec'] in _EXPONENTIAL_QUALITY_CODECS: - vbr = max(1, _MAX_FFMPEG_VARIABLE_BITRATE * (1 - math.log(max(1, q * _EXPONENTIAL_QUALITY_FACTOR), _EXPONENTIAL_QUALITY_FACTOR * 100))) + vbr = max(1, _MAX_FFMPEG_VARIABLE_BITRATE * (1 - math.log(max(1, q * _EXPONENTIAL_QUALITY_FACTOR), + _EXPONENTIAL_QUALITY_FACTOR * 100))) else: vbr = 1 + (_MAX_FFMPEG_VARIABLE_BITRATE - 1) / 100.0 * (100 - q) @@ -950,7 +954,8 @@ def motion_camera_ui_to_dict(ui, old_config=None): if data.get('rotate') in [90, 270]: capture_width, capture_height = capture_height, capture_width - data['mask_file'] = utils.build_editable_mask_file(old_config['@id'], ui['mask_lines'], capture_width, capture_height) + data['mask_file'] = utils.build_editable_mask_file(old_config['@id'], ui['mask_lines'], + capture_width, capture_height) # working schedule if ui['working_schedule']: @@ -970,7 +975,8 @@ def motion_camera_ui_to_dict(ui, old_config=None): if ui['email_notifications_enabled']: emails = re.sub('\\s', '', ui['email_notifications_addresses']) - on_event_start.append("%(script)s '%(server)s' '%(port)s' '%(account)s' '%(password)s' '%(tls)s' '%(from)s' '%(to)s' 'motion_start' '%%t' '%%Y-%%m-%%dT%%H:%%M:%%S' '%(timespan)s'" % { + line = "%(script)s '%(server)s' '%(port)s' '%(account)s' '%(password)s' '%(tls)s' '%(from)s' '%(to)s' " \ + "'motion_start' '%%t' '%%Y-%%m-%%dT%%H:%%M:%%S' '%(timespan)s'" % { 'script': meyectl.find_command('sendmail'), 'server': ui['email_notifications_smtp_server'], 'port': ui['email_notifications_smtp_port'], @@ -979,7 +985,9 @@ def motion_camera_ui_to_dict(ui, old_config=None): 'tls': ui['email_notifications_smtp_tls'], 'from': ui['email_notifications_from'], 'to': emails, - 'timespan': ui['email_notifications_picture_time_span']}) + 'timespan': ui['email_notifications_picture_time_span']} + + on_event_start.append(line) if ui['web_hook_notifications_enabled']: url = re.sub('\\s', '+', ui['web_hook_notifications_url']) @@ -1085,7 +1093,7 @@ def motion_camera_dict_to_ui(data): 'upload_subfolders': data['@upload_subfolders'], 'upload_username': data['@upload_username'], 'upload_password': data['@upload_password'], - 'upload_authorization_key': '', # needed, otherwise the field is hidden + 'upload_authorization_key': '', # needed, otherwise the field is hidden 'web_hook_storage_enabled': False, 'command_storage_enabled': False, @@ -1169,11 +1177,11 @@ def motion_camera_dict_to_ui(data): threshold = data['threshold'] * 100.0 / (data['width'] * data['height']) - else: # width & height are not available for other netcams + else: # width & height are not available for other netcams # we have no other choice but use something like 640x480 as reference threshold = data['threshold'] * 100.0 / (640 * 480) - else: # assuming v4l2 + else: # assuming v4l2 ui['device_url'] = data['videodevice'] ui['proto'] = 'v4l2' @@ -1186,7 +1194,7 @@ def motion_camera_dict_to_ui(data): # indicate the presence of these controls # we must call v4l2ctl functions to determine the available controls brightness = v4l2ctl.get_brightness(data['videodevice']) - if brightness is not None: # has brightness control + if brightness is not None: # has brightness control if data.get('brightness', 0) != 0: ui['brightness'] = brightness @@ -1194,7 +1202,7 @@ def motion_camera_dict_to_ui(data): ui['brightness'] = 50 contrast = v4l2ctl.get_contrast(data['videodevice']) - if contrast is not None: # has contrast control + if contrast is not None: # has contrast control if data.get('contrast', 0) != 0: ui['contrast'] = contrast @@ -1202,7 +1210,7 @@ def motion_camera_dict_to_ui(data): ui['contrast'] = 50 saturation = v4l2ctl.get_saturation(data['videodevice']) - if saturation is not None: # has saturation control + if saturation is not None: # has saturation control if data.get('saturation', 0) != 0: ui['saturation'] = saturation @@ -1210,7 +1218,7 @@ def motion_camera_dict_to_ui(data): ui['saturation'] = 50 hue = v4l2ctl.get_hue(data['videodevice']) - if hue is not None: # has hue control + if hue is not None: # has hue control if data.get('hue', 0) != 0: ui['hue'] = hue @@ -1222,7 +1230,10 @@ def motion_camera_dict_to_ui(data): ui['frame_change_threshold'] = threshold if (data['@storage_device'] == 'network-share') and settings.SMB_SHARES: - mount_point = smbctl.make_mount_point(data['@network_server'], data['@network_share_name'], data['@network_username']) + mount_point = smbctl.make_mount_point(data['@network_server'], + data['@network_share_name'], + data['@network_username']) + ui['root_directory'] = data['target_dir'][len(mount_point):] or '/' elif data['@storage_device'].startswith('local-disk'): @@ -1233,8 +1244,10 @@ def motion_camera_dict_to_ui(data): ui['root_directory'] = data['target_dir'][len(partition['mount_point']):] or '/' break - else: # not found for some reason - logging.error('could not find mounted partition for device "%s" and target dir "%s"' % (target_dev, data['target_dir'])) + else: # not found for some reason + logging.error('could not find mounted partition for device "%s" and target dir "%s"' % + (target_dev, data['target_dir'])) + ui['root_directory'] = data['target_dir'] else: @@ -1317,7 +1330,8 @@ def motion_camera_dict_to_ui(data): bitrate = data['ffmpeg_variable_bitrate'] if motionctl.needs_ffvb_quirks(): if data['ffmpeg_video_codec'] in _EXPONENTIAL_QUALITY_CODECS: - q = (100 * _EXPONENTIAL_QUALITY_FACTOR) ** ((1 - float(bitrate) / _MAX_FFMPEG_VARIABLE_BITRATE)) / _EXPONENTIAL_QUALITY_FACTOR + q = (100 * _EXPONENTIAL_QUALITY_FACTOR) ** \ + (1 - float(bitrate) / _MAX_FFMPEG_VARIABLE_BITRATE) / _EXPONENTIAL_QUALITY_FACTOR else: q = 100 - (bitrate - 1) * 100.0 / (_MAX_FFMPEG_VARIABLE_BITRATE - 1) @@ -1400,9 +1414,9 @@ def motion_camera_dict_to_ui(data): ui['web_hook_notifications_url'] = e[-1] elif e.count('relayevent'): - continue # ignore internal relay script + continue # ignore internal relay script - else: # custom command + else: # custom command command_notifications.append(e) if command_notifications: @@ -1427,9 +1441,9 @@ def motion_camera_dict_to_ui(data): ui['web_hook_storage_url'] = e[-1] elif e.count('relayevent'): - continue # ignore internal relay script + continue # ignore internal relay script - else: # custom command + else: # custom command command_storage.append(e) if command_storage: @@ -1448,7 +1462,7 @@ def motion_camera_dict_to_ui(data): for name, value in data.iteritems(): if name not in _KNOWN_MOTION_OPTIONS and not name.startswith('@'): if isinstance(value, bool): - value = ['off', 'on'][value] # boolean values should be transferred as on/off + value = ['off', 'on'][value] # boolean values should be transferred as on/off extra_options.append((name, value)) @@ -1535,7 +1549,9 @@ def backup(): logging.debug('generating config backup file') if len(os.listdir(settings.CONF_PATH)) > 100: - logging.debug('config path "%s" appears to be a system-wide config directory, performing a selective backup' % settings.CONF_PATH) + logging.debug('config path "%s" appears to be a system-wide config directory, performing a selective backup' % + settings.CONF_PATH) + cmd = ['tar', 'zc', 'motion.conf'] cmd += map(os.path.basename, glob.glob(os.path.join(settings.CONF_PATH, 'thread-*.conf'))) try: @@ -1550,7 +1566,8 @@ def backup(): return None else: - logging.debug('config path "%s" appears to be a motion-specific config directory, performing a full backup' % settings.CONF_PATH) + logging.debug('config path "%s" appears to be a motion-specific config directory, performing a full backup' % + settings.CONF_PATH) try: content = subprocess.check_output(['tar', 'zc', '.'], cwd=settings.CONF_PATH) @@ -1647,24 +1664,30 @@ def _python_to_value(value): return value -def _conf_to_dict(lines, list_names=[], no_convert=[]): - data = utils.OrderedDict() +def _conf_to_dict(lines, list_names=None, no_convert=None): + if list_names is None: + list_names = [] + + if no_convert is None: + no_convert = [] + + data = collections.OrderedDict() for line in lines: line = line.strip() if len(line) == 0: # empty line continue - match = re.match('^\#\s*(\@\w+)\s*(.*)', line) + match = re.match('^#\s*(@\w+)\s*(.*)', line) if match: name, value = match.groups()[:2] - elif line.startswith('#') or line.startswith(';'): # comment line + elif line.startswith('#') or line.startswith(';'): # comment line continue else: parts = line.split(None, 1) - if len(parts) == 1: # empty value + if len(parts) == 1: # empty value parts.append('') (name, value) = parts @@ -1683,9 +1706,12 @@ def _conf_to_dict(lines, list_names=[], no_convert=[]): return data -def _dict_to_conf(lines, data, list_names=[]): +def _dict_to_conf(lines, data, list_names=None): + if list_names is None: + list_names = [] + conf_lines = [] - remaining = utils.OrderedDict(data) + remaining = collections.OrderedDict(data) processed = set() # parse existing lines and replace the values @@ -1696,8 +1722,8 @@ def _dict_to_conf(lines, data, list_names=[]): conf_lines.append(line) continue - match = re.match('^\#\s*(\@\w+)\s*(.*)', line) - if match: # @line + match = re.match('^#\s*(@\w+)\s*(.*)', line) + if match: # @line (name, value) = match.groups()[:2] elif line.startswith('#') or line.startswith(';'): # simple comment line @@ -1713,7 +1739,7 @@ def _dict_to_conf(lines, data, list_names=[]): (name, value) = parts[0], '' if name in processed: - continue # name already processed + continue # name already processed processed.add(name) @@ -1743,11 +1769,11 @@ def _dict_to_conf(lines, data, list_names=[]): # add the remaining config values not covered by existing lines if len(remaining) and len(lines): - conf_lines.append('') # add a blank line + conf_lines.append('') # add a blank line for (name, value) in remaining.iteritems(): if name.startswith('@_'): - continue # ignore additional configs + continue # ignore additional configs if name in list_names: for v in value: @@ -1774,7 +1800,7 @@ def _dict_to_conf(lines, data, list_names=[]): line = '# ' + line elif i > 0 and conf_lines[i - 1].startswith('@'): - lines.append('') # add a blank line between @lines and the rest + lines.append('') # add a blank line between @lines and the rest lines.append(line) @@ -1885,12 +1911,12 @@ def _set_default_motion_camera(camera_id, data): data.setdefault('max_movie_time', 0) data.setdefault('ffmpeg_output_movies', False) if motionctl.has_new_movie_format_support(): - data.setdefault('ffmpeg_video_codec', 'mp4') # will use h264 codec + data.setdefault('ffmpeg_video_codec', 'mp4') # will use h264 codec if motionctl.needs_ffvb_quirks(): - data.setdefault('ffmpeg_variable_bitrate', _MAX_FFMPEG_VARIABLE_BITRATE / 4) # 75% + data.setdefault('ffmpeg_variable_bitrate', _MAX_FFMPEG_VARIABLE_BITRATE / 4) # 75% else: - data.setdefault('ffmpeg_variable_bitrate', 75) # 75% + data.setdefault('ffmpeg_variable_bitrate', 75) # 75% else: data.setdefault('ffmpeg_video_codec', 'msmpeg4') @@ -1919,7 +1945,7 @@ def get_additional_structure(camera, separators=False): 'with' if separators else 'without')) # gather sections - sections = utils.OrderedDict() + sections = collections.OrderedDict() for func in _additional_section_funcs: result = func() if not result: @@ -1936,7 +1962,7 @@ def get_additional_structure(camera, separators=False): logging.debug('additional config section: %s' % result['name']) - configs = utils.OrderedDict() + configs = collections.OrderedDict() for func in _additional_config_funcs: result = func() if not result: diff --git a/motioneye/diskctl.py b/motioneye/diskctl.py index 8691474..8d7a4b1 100644 --- a/motioneye/diskctl.py +++ b/motioneye/diskctl.py @@ -46,12 +46,12 @@ def _list_mounts(): continue if target in seen_targets: - continue # probably a bind mount + continue # probably a bind mount seen_targets.add(target) if fstype == 'fuseblk': - fstype = 'ntfs' # most likely + fstype = 'ntfs' # most likely logging.debug('found mount "%s" at "%s"' % (target, mount_point)) @@ -161,15 +161,15 @@ def _list_disks_fdisk(): disks = [] disk = None - def add_disk(disk): + def add_disk(d): logging.debug('found disk at "%s" on bus "%s": "%s %s"' % - (disk['target'], disk['bus'], disk['vendor'], disk['model'])) + (d['target'], d['bus'], d['vendor'], d['model'])) - for part in disk['partitions']: + for part in d['partitions']: logging.debug('found partition "%s" at "%s" on bus "%s": "%s %s"' % (part['part_no'], part['target'], part['bus'], part['vendor'], part['model'])) - disks.append(disk) + disks.append(d) for line in output.split('\n'): line = line.replace('*', '') diff --git a/motioneye/handlers.py b/motioneye/handlers.py index 051621c..587372a 100644 --- a/motioneye/handlers.py +++ b/motioneye/handlers.py @@ -76,16 +76,16 @@ class BaseHandler(RequestHandler): return self._json - def get_argument(self, name, default=None): - DEF = {} - argument = RequestHandler.get_argument(self, name, default=DEF) - if argument is DEF: + def get_argument(self, name, default=None, strip=True): + def_ = {} + argument = RequestHandler.get_argument(self, name, default=def_) + if argument is def_: # try to find it in json body data = self.get_json() if data: - argument = data.get(name, DEF) + argument = data.get(name, def_) - if argument is DEF: + if argument is def_: argument = default return argument @@ -101,8 +101,11 @@ class BaseHandler(RequestHandler): content = template.render(template_name, **context) self.finish(content) - - def finish_json(self, data={}): + + def finish_json(self, data=None): + if data is None: + data = {} + self.set_header('Content-Type', 'application/json') self.finish(json.dumps(data)) @@ -113,7 +116,7 @@ class BaseHandler(RequestHandler): signature = self.get_argument('_signature', None) login = self.get_argument('_login', None) == 'true' if (username == main_config.get('@admin_username') and - (signature == utils.compute_signature(self.request.method, self.request.uri, # backwards compatibility + (signature == utils.compute_signature(self.request.method, self.request.uri, # backwards compatibility self.request.body, main_config['@admin_password']) or signature == utils.compute_signature(self.request.method, self.request.uri, self.request.body, @@ -121,11 +124,11 @@ class BaseHandler(RequestHandler): return 'admin' - elif not username and not main_config.get('@normal_password'): # no authentication required for normal user + elif not username and not main_config.get('@normal_password'): # no authentication required for normal user return 'normal' elif (username == main_config.get('@normal_username') and - (signature == utils.compute_signature(self.request.method, self.request.uri, # backwards compatibility + (signature == utils.compute_signature(self.request.method, self.request.uri, # backwards compatibility self.request.body, main_config.get('@normal_password')) or signature == utils.compute_signature(self.request.method, self.request.uri, self.request.body, @@ -149,7 +152,8 @@ class BaseHandler(RequestHandler): if isinstance(exception, HTTPError): logging.error(str(exception)) self.set_status(exception.status_code) - self.finish_json({'error': exception.log_message or getattr(exception, 'reason', None) or str(exception)}) + self.finish_json({'error': exception.log_message or + getattr(exception, 'reason', None) or str(exception)}) else: logging.error(str(exception), exc_info=True) @@ -157,7 +161,7 @@ class BaseHandler(RequestHandler): self.finish_json({'error': 'internal server error'}) except RuntimeError: - pass # nevermind + pass # nevermind @staticmethod def auth(admin=False, prompt=True): @@ -230,7 +234,7 @@ class ManifestHandler(BaseHandler): import motioneye self.set_header('Content-Type', 'application/manifest+json') - self.set_header('Cache-Control', 'max-age=2592000') # 30 days + self.set_header('Cache-Control', 'max-age=2592000') # 30 days self.render('manifest.json', version=motioneye.VERSION) @@ -300,8 +304,10 @@ class ConfigHandler(BaseHandler): elif utils.is_remote_camera(local_config): def on_response(remote_ui_config=None, error=None): if error: - return self.finish_json({'error': 'Failed to get remote camera configuration for %(url)s: %(msg)s.' % { - 'url': remote.pretty_camera_url(local_config), 'msg': error}}) + msg = 'Failed to get remote camera configuration for %(url)s: %(msg)s.' % { + 'url': remote.pretty_camera_url(local_config), 'msg': error} + + return self.finish_json({'error': msg}) for key, value in local_config.items(): remote_ui_config[key.replace('@', '')] = value @@ -312,7 +318,7 @@ class ConfigHandler(BaseHandler): remote.get_config(local_config, on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera ui_config = config.simple_mjpeg_camera_dict_to_ui(local_config) self.finish_json(ui_config) @@ -347,18 +353,18 @@ class ConfigHandler(BaseHandler): config.set_camera(camera_id, local_config) - on_finish(None, True) # (no error, motion needs restart) + on_finish(None, True) # (no error, motion needs restart) elif utils.is_remote_camera(local_config): # update the camera locally local_config['@enabled'] = ui_config['enabled'] config.set_camera(camera_id, local_config) - if ui_config.has_key('name'): - def on_finish_wrapper(error=None): - return on_finish(error, False) + if 'name' in ui_config: + def on_finish_wrapper(e=None): + return on_finish(e, False) - ui_config['enabled'] = True # never disable the camera remotely + ui_config['enabled'] = True # never disable the camera remotely remote.set_config(local_config, ui_config, on_finish_wrapper) else: @@ -367,24 +373,28 @@ class ConfigHandler(BaseHandler): # the camera was probably disabled due to errors on_finish(None, False) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera local_config = config.simple_mjpeg_camera_ui_to_dict(ui_config, local_config) config.set_camera(camera_id, local_config) - on_finish(None, False) # (no error, motion doesn't need restart) + on_finish(None, False) # (no error, motion doesn't need restart) def set_main_config(ui_config): logging.debug('setting main config...') old_main_config = config.get_main() - old_admin_credentials = '%s:%s' % (old_main_config.get('@admin_username', ''), old_main_config.get('@admin_password', '')) - old_normal_credentials = '%s:%s' % (old_main_config.get('@normal_username', ''), old_main_config.get('@normal_password', '')) + old_admin_credentials = '%s:%s' % (old_main_config.get('@admin_username', ''), + old_main_config.get('@admin_password', '')) + old_normal_credentials = '%s:%s' % (old_main_config.get('@normal_username', ''), + old_main_config.get('@normal_password', '')) main_config = config.main_ui_to_dict(ui_config) main_config.setdefault('thread', old_main_config.get('thread', [])) - admin_credentials = '%s:%s' % (main_config.get('@admin_username', ''), main_config.get('@admin_password', '')) - normal_credentials = '%s:%s' % (main_config.get('@normal_username', ''), main_config.get('@normal_password', '')) + admin_credentials = '%s:%s' % (main_config.get('@admin_username', ''), + main_config.get('@admin_password', '')) + normal_credentials = '%s:%s' % (main_config.get('@normal_username', ''), + main_config.get('@normal_password', '')) additional_configs = config.get_additional_structure(camera=False)[1] reboot_config_names = [('@_' + c['name']) for c in additional_configs.values() if c.get('reboot')] @@ -425,8 +435,8 @@ class ConfigHandler(BaseHandler): return {'reload': reload, 'reboot': reboot, 'restart': restart} - reload = False # indicates that browser should reload the page - reboot = [False] # indicates that the server will reboot immediately + reload = False # indicates that browser should reload the page + reboot = [False] # indicates that the server will reboot immediately restart = [False] # indicates that the local motion instance was modified and needs to be restarted error = [None] @@ -461,7 +471,7 @@ class ConfigHandler(BaseHandler): self.finish({'reload': reload, 'reboot': reboot[0], 'error': error[0]}) if camera_id is not None: - if camera_id == 0: # multiple camera configs + if camera_id == 0: # multiple camera configs if len(ui_config) > 1: logging.debug('setting multiple configs') @@ -471,12 +481,13 @@ class ConfigHandler(BaseHandler): self.finish() so_far = [0] + def check_finished(e, r): restart[0] = restart[0] or r error[0] = error[0] or e so_far[0] += 1 - if so_far[0] >= len(ui_config): # finished + if so_far[0] >= len(ui_config): # finished finish() # make sure main config is handled first @@ -494,7 +505,7 @@ class ConfigHandler(BaseHandler): else: set_camera_config(int(key), cfg, check_finished) - else: # single camera config + else: # single camera config def on_finish(e, r): error[0] = e restart[0] = r @@ -502,7 +513,7 @@ class ConfigHandler(BaseHandler): set_camera_config(camera_id, ui_config, on_finish) - else: # main config + else: # main config result = set_main_config(ui_config) reload = result['reload'] reboot[0] = result['reboot'] @@ -558,7 +569,7 @@ class ConfigHandler(BaseHandler): remote.set_preview(camera_config, controls, on_response) - else: # not supported + else: # not supported self.finish_json({'error': True}) @BaseHandler.auth() @@ -587,7 +598,8 @@ class ConfigHandler(BaseHandler): self.finish_json({'cameras': cameras}) if scheme in ['http', 'https']: - utils.test_mjpeg_url(self.get_all_arguments(), auth_modes=['basic'], allow_jpeg=True, callback=on_response) + utils.test_mjpeg_url(self.get_all_arguments(), auth_modes=['basic'], allow_jpeg=True, + callback=on_response) elif motionctl.get_rtsp_support() and scheme == 'rtsp': utils.test_rtsp_url(self.get_all_arguments(), callback=on_response) @@ -603,7 +615,8 @@ class ConfigHandler(BaseHandler): else: self.finish_json({'cameras': cameras}) - utils.test_mjpeg_url(self.get_all_arguments(), auth_modes=['basic', 'digest'], allow_jpeg=False, callback=on_response) + utils.test_mjpeg_url(self.get_all_arguments(), auth_modes=['basic', 'digest'], allow_jpeg=False, + callback=on_response) elif proto == 'v4l2': configured_devices = set() @@ -624,6 +637,7 @@ class ConfigHandler(BaseHandler): camera_ids = [] length = [len(camera_ids)] + def check_finished(): if len(cameras) == length[0]: cameras.sort(key=lambda c: c['id']) @@ -675,10 +689,10 @@ class ConfigHandler(BaseHandler): if local_config.get('@enabled') or self.get_argument('force', None) == 'true': remote.get_config(local_config, on_response_builder(camera_id, local_config)) - else: # don't try to reach the remote of the camera is disabled + else: # don't try to reach the remote of the camera is disabled on_response_builder(camera_id, local_config)(error=True) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera ui_config = config.simple_mjpeg_camera_dict_to_ui(local_config) cameras.append(ui_config) check_finished() @@ -728,7 +742,7 @@ class ConfigHandler(BaseHandler): remote.get_config(camera_config, on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera ui_config = config.simple_mjpeg_camera_dict_to_ui(camera_config) self.finish_json(ui_config) @@ -792,7 +806,6 @@ class ConfigHandler(BaseHandler): logging.warn('accessing %s failed: %s' % (service_name, result)) request_handler.finish_json({'error': result}) - @BaseHandler.auth(admin=True) def test(self, camera_id): what = self.get_argument('what') @@ -804,7 +817,7 @@ class ConfigHandler(BaseHandler): service_name = data['service'] ConfigHandler._upload_service_test_info = (self, service_name) - tasks.add(0, uploadservices.test_access, tag='uploadservices.test(%s)'% service_name, + tasks.add(0, uploadservices.test_access, tag='uploadservices.test(%s)' % service_name, camera_id=camera_id, service_name=service_name, data=data, callback=self._on_test_result) elif what == 'email': @@ -833,8 +846,10 @@ class ConfigHandler(BaseHandler): old_timeout = settings.SMTP_TIMEOUT settings.SMTP_TIMEOUT = 10 - sendmail.send_mail(data['smtp_server'], int(data['smtp_port']), data['smtp_account'], data['smtp_password'], data['smtp_tls'], - data['from'], [data['addresses']], subject=subject, message=message, files=[]) + sendmail.send_mail(data['smtp_server'], int(data['smtp_port']), data['smtp_account'], + data['smtp_password'], data['smtp_tls'], data['from'], [data['addresses']], + subject=subject, message=message, files=[]) + settings.SMTP_TIMEOUT = old_timeout self.finish_json() @@ -868,7 +883,9 @@ class ConfigHandler(BaseHandler): logging.debug('testing access to network share //%s/%s' % (data['server'], data['share'])) try: - smbctl.test_share(data['server'], data['share'], data['username'], data['password'], data['root_directory']) + smbctl.test_share(data['server'], data['share'], data['username'], + data['password'], data['root_directory']) + logging.debug('access to network share //%s/%s succeeded' % (data['server'], data['share'])) self.finish_json() @@ -941,7 +958,7 @@ class PictureHandler(BaseHandler): @asynchronous def post(self, camera_id, op, filename=None, group=None): - if group == '/': # ungrouped + if group == '/': # ungrouped group = '' if camera_id is not None: @@ -1000,10 +1017,9 @@ class PictureHandler(BaseHandler): remote.get_current_picture(camera_config, width=width, height=height, callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') - @BaseHandler.auth() def list(self, camera_id): logging.debug('listing pictures for camera %(id)s' % {'id': camera_id}) @@ -1030,15 +1046,19 @@ class PictureHandler(BaseHandler): self.finish_json(remote_list) - remote.list_media(camera_config, media_type='picture', prefix=self.get_argument('prefix', None), callback=on_response) + remote.list_media(camera_config, media_type='picture', prefix=self.get_argument('prefix', None), + callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') def frame(self, camera_id): camera_config = config.get_camera(camera_id) - if utils.is_local_motion_camera(camera_config) or utils.is_simple_mjpeg_camera(camera_config) or self.get_argument('title', None) is not None: + if (utils.is_local_motion_camera(camera_config) or + utils.is_simple_mjpeg_camera(camera_config) or + self.get_argument('title', None) is not None): + self.render('main.html', frame=True, camera_id=camera_id, @@ -1090,7 +1110,7 @@ class PictureHandler(BaseHandler): return self.finish_json({'error': 'Failed to download picture from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) - pretty_filename = os.path.basename(filename) # no camera name available w/o additional request + 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 + ';') @@ -1098,7 +1118,7 @@ class PictureHandler(BaseHandler): remote.get_media_content(camera_config, filename=filename, media_type='picture', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth() @@ -1137,7 +1157,7 @@ class PictureHandler(BaseHandler): height=self.get_argument('height', None), callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth(admin=True) @@ -1164,7 +1184,7 @@ class PictureHandler(BaseHandler): remote.del_media_content(camera_config, filename=filename, media_type='picture', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth() @@ -1200,12 +1220,13 @@ class PictureHandler(BaseHandler): self.set_header('Content-Disposition', response['content_disposition']) self.finish(response['data']) - remote.get_zipped_content(camera_config, media_type='picture', key=key, group=group, callback=on_response) + remote.get_zipped_content(camera_config, media_type='picture', key=key, group=group, + callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') - else: # prepare + else: # prepare logging.debug('preparing zip file for group "%(group)s" of camera %(id)s' % { 'group': group or 'ungrouped', 'id': camera_id}) @@ -1231,7 +1252,7 @@ class PictureHandler(BaseHandler): remote.make_zipped_content(camera_config, media_type='picture', group=group, callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth() @@ -1240,7 +1261,7 @@ class PictureHandler(BaseHandler): check = self.get_argument('check', False) camera_config = config.get_camera(camera_id) - if key: # download + 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}) @@ -1262,8 +1283,10 @@ class PictureHandler(BaseHandler): elif utils.is_remote_camera(camera_config): def on_response(response=None, error=None): if error: - return self.finish_json({'error': 'Failed to download timelapse movie from %(url)s: %(msg)s.' % { - 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) + msg = 'Failed to download timelapse movie from %(url)s: %(msg)s.' % { + 'url': remote.pretty_camera_url(camera_config), 'msg': error} + + return self.finish_json({'error': msg}) self.set_header('Content-Type', response['content_type']) self.set_header('Content-Disposition', response['content_disposition']) @@ -1271,7 +1294,7 @@ class PictureHandler(BaseHandler): remote.get_timelapse_movie(camera_config, key, group=group, callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') elif check: @@ -1292,8 +1315,10 @@ class PictureHandler(BaseHandler): elif utils.is_remote_camera(camera_config): def on_response(response=None, error=None): if error: - return self.finish_json({'error': 'Failed to check timelapse movie progress at %(url)s: %(msg)s.' % { - 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) + msg = 'Failed to check timelapse movie progress at %(url)s: %(msg)s.' % { + 'url': remote.pretty_camera_url(camera_config), 'msg': error} + + return self.finish_json({'error': msg}) if response['progress'] == -1 and response.get('key'): self.finish_json({'key': response['key'], 'progress': -1}) @@ -1303,20 +1328,21 @@ class PictureHandler(BaseHandler): remote.check_timelapse_movie(camera_config, group=group, callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') - else: # start timelapse + else: # start timelapse interval = int(self.get_argument('interval')) framerate = int(self.get_argument('framerate')) - logging.debug('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}) + 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: - self.finish_json({'progress': status['progress']}) # timelapse already active + self.finish_json({'progress': status['progress']}) # timelapse already active else: mediafiles.make_timelapse_movie(camera_config, framerate, interval, group=group) @@ -1329,7 +1355,7 @@ class PictureHandler(BaseHandler): 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) if response['progress'] != -1: - return self.finish_json({'progress': response['progress']}) # timelapse already active + return self.finish_json({'progress': response['progress']}) # timelapse already active def on_make(response=None, error=None): if error: @@ -1342,7 +1368,7 @@ class PictureHandler(BaseHandler): remote.check_timelapse_movie(camera_config, group=group, callback=on_status) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth(admin=True) @@ -1369,7 +1395,7 @@ class PictureHandler(BaseHandler): remote.del_media_group(camera_config, group=group, media_type='picture', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') def try_finish(self, content): @@ -1402,7 +1428,7 @@ class MovieHandler(BaseHandler): @asynchronous def post(self, camera_id, op, filename=None, group=None): - if group == '/': # ungrouped + if group == '/': # ungrouped group = '' if camera_id is not None: @@ -1445,9 +1471,10 @@ class MovieHandler(BaseHandler): self.finish_json(remote_list) - remote.list_media(camera_config, media_type='movie', prefix=self.get_argument('prefix', None), callback=on_response) + remote.list_media(camera_config, media_type='movie', prefix=self.get_argument('prefix', None), + callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth() @@ -1471,7 +1498,7 @@ class MovieHandler(BaseHandler): return self.finish_json({'error': 'Failed to download movie from %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) - pretty_filename = os.path.basename(filename) # no camera name available w/o additional request + pretty_filename = os.path.basename(filename) # no camera name available w/o additional request self.set_header('Content-Type', 'video/mpeg') self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';') @@ -1479,7 +1506,7 @@ class MovieHandler(BaseHandler): remote.get_media_content(camera_config, filename=filename, media_type='movie', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth() @@ -1518,7 +1545,7 @@ class MovieHandler(BaseHandler): height=self.get_argument('height', None), callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth(admin=True) @@ -1545,7 +1572,7 @@ class MovieHandler(BaseHandler): remote.del_media_content(camera_config, filename=filename, media_type='movie', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @BaseHandler.auth(admin=True) @@ -1572,7 +1599,7 @@ class MovieHandler(BaseHandler): remote.del_media_group(camera_config, group=group, media_type='movie', callback=on_response) - else: # assuming simple mjpeg camera + else: # assuming simple mjpeg camera raise HTTPError(400, 'unknown operation') @@ -1587,8 +1614,10 @@ class ActionHandler(BaseHandler): if utils.is_remote_camera(local_config): def on_response(error=None): if error: - return self.finish_json({'error': 'Failed to execute action on remote camera at %(url)s: %(msg)s.' % { - 'url': remote.pretty_camera_url(local_config), 'msg': error}}) + msg = 'Failed to execute action on remote camera at %(url)s: %(msg)s.' % { + 'url': remote.pretty_camera_url(local_config), 'msg': error} + + return self.finish_json({'error': msg}) self.finish_json() @@ -1682,7 +1711,7 @@ class RelayEventHandler(BaseHandler): return self.finish_json() else: - logging.debug('recevied relayed event %(event)s for thread id %(id)s (camera id %(cid)s)' % { + logging.debug('received relayed event %(event)s for thread id %(id)s (camera id %(cid)s)' % { 'event': event, 'id': thread_id, 'cid': camera_id}) camera_config = config.get_camera(camera_id) @@ -1748,13 +1777,13 @@ class LogHandler(BaseHandler): self.set_header('Content-Type', 'text/plain') self.set_header('Content-Disposition', 'attachment; filename=' + filename + ';') - if path.startswith('/'): # an actual path + if path.startswith('/'): # an actual path logging.debug('serving log file "%s" from "%s"' % (filename, path)) with open(path) as f: self.finish(f.read()) - else: # a command to execute + else: # a command to execute logging.debug('serving log file "%s" from command "%s"' % (filename, path)) try: diff --git a/motioneye/mediafiles.py b/motioneye/mediafiles.py index 680cdd9..fc7afb6 100644 --- a/motioneye/mediafiles.py +++ b/motioneye/mediafiles.py @@ -101,14 +101,14 @@ def findfiles(path): return files -def _list_media_files(dir, exts, prefix=None): +def _list_media_files(directory, exts, prefix=None): media_files = [] if prefix is not None: if prefix == 'ungrouped': prefix = '' - root = os.path.join(dir, prefix) + root = os.path.join(directory, prefix) for name in os.listdir(root): # ignore hidden files/dirs and other unwanted files if name.startswith('.') or name == 'lastsnap.jpg': @@ -122,7 +122,7 @@ def _list_media_files(dir, exts, prefix=None): logging.error('stat failed: ' + unicode(e)) continue - if not stat.S_ISREG(st.st_mode): # not a regular file + if not stat.S_ISREG(st.st_mode): # not a regular file continue full_path_lower = full_path.lower() @@ -132,7 +132,7 @@ def _list_media_files(dir, exts, prefix=None): media_files.append((full_path, st)) else: - for full_path, name, st in findfiles(dir): + for full_path, name, st in findfiles(directory): full_path_lower = full_path.lower() if not [e for e in exts if full_path_lower.endswith(e)]: continue @@ -142,8 +142,8 @@ def _list_media_files(dir, exts, prefix=None): return media_files -def _remove_older_files(dir, moment, exts): - for (full_path, st) in _list_media_files(dir, exts): +def _remove_older_files(directory, moment, exts): + for (full_path, st) in _list_media_files(directory, exts): file_moment = datetime.datetime.fromtimestamp(st.st_mtime) if file_moment < moment: logging.debug('removing file %(path)s...' % {'path': full_path}) @@ -154,7 +154,7 @@ def _remove_older_files(dir, moment, exts): except OSError as e: if e.errno == errno.ENOENT: - pass # the file might have been removed in the meantime + pass # the file might have been removed in the meantime else: logging.error('failed to remove %s: %s' % (full_path, e)) @@ -167,7 +167,7 @@ def _remove_older_files(dir, moment, exts): listing = os.listdir(dir_path) thumbs = [l for l in listing if l.endswith('.thumb')] - if len(listing) == len(thumbs): # only thumbs + if len(listing) == len(thumbs): # only thumbs for p in thumbs: try: os.remove(os.path.join(dir_path, p)) @@ -190,7 +190,7 @@ def find_ffmpeg(): try: return subprocess.check_output(['which', 'ffmpeg'], stderr=utils.DEV_NULL).strip() - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None @@ -200,9 +200,9 @@ def cleanup_media(media_type): if media_type == 'picture': exts = _PICTURE_EXTS - elif media_type == 'movie': + else: # media_type == 'movie' exts = _MOVIE_EXTS + ['.thumb'] - + for camera_id in config.get_camera_ids(): camera_config = config.get_camera(camera_id) if not utils.is_local_motion_camera(camera_config): @@ -210,7 +210,7 @@ def cleanup_media(media_type): preserve_media = camera_config.get('@preserve_%(media_type)ss' % {'media_type': media_type}, 0) if preserve_media == 0: - continue # preserve forever + continue # preserve forever still_images_enabled = bool( ((camera_config['emulate_motion'] or camera_config['output_pictures']) and camera_config['picture_filename']) or @@ -219,10 +219,10 @@ def cleanup_media(media_type): movies_enabled = camera_config['ffmpeg_output_movies'] if media_type == 'picture' and not still_images_enabled: - continue # only cleanup pictures for cameras with still images enabled + continue # only cleanup pictures for cameras with still images enabled elif media_type == 'movie' and not movies_enabled: - continue # only cleanup movies for cameras with movies enabled + continue # only cleanup movies for cameras with movies enabled preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_media) @@ -348,24 +348,24 @@ def list_media(camera_config, media_type, callback, prefix=None): def poll_process(): io_loop = IOLoop.instance() - if process.is_alive(): # not finished yet + if process.is_alive(): # not finished yet now = datetime.datetime.now() delta = now - started if delta.seconds < settings.LIST_MEDIA_TIMEOUT: io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_process) read_media_list() - else: # process did not finish in time + else: # process did not finish in time logging.error('timeout waiting for the media listing process to finish') try: os.kill(process.pid, signal.SIGTERM) except: - pass # nevermind + pass # nevermind callback(None) - else: # finished + else: # finished read_media_list() logging.debug('media listing process has returned %(count)s files' % {'count': len(media_list)}) callback(media_list) @@ -463,17 +463,17 @@ def get_zipped_content(camera_config, media_type, group, callback): if delta.seconds < settings.ZIP_TIMEOUT: io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_process) - else: # process did not finish in time + else: # process did not finish in time logging.error('timeout waiting for the zip process to finish') try: os.kill(process.pid, signal.SIGTERM) except: - pass # nevermind + pass # nevermind callback(None) - else: # finished + else: # finished try: data = parent_pipe.recv() logging.debug('zip process has returned %d bytes' % len(data)) @@ -494,7 +494,7 @@ def make_timelapse_movie(camera_config, framerate, interval, group): codec = camera_config.get('ffmpeg_video_codec') codec = FFMPEG_CODEC_MAPPING.get(codec, codec) - format = FFMPEG_FORMAT_MAPPING.get(codec, codec) + fmt = FFMPEG_FORMAT_MAPPING.get(codec, codec) # create a subprocess to retrieve media files def do_list_media(pipe): @@ -528,24 +528,24 @@ def make_timelapse_movie(camera_config, framerate, interval, group): def poll_media_list_process(): io_loop = IOLoop.instance() - if _timelapse_process.is_alive(): # not finished yet + if _timelapse_process.is_alive(): # not finished yet now = datetime.datetime.now() delta = now - started[0] - if delta.seconds < settings.TIMELAPSE_TIMEOUT: # the subprocess has limited time to complete its job + if delta.seconds < settings.TIMELAPSE_TIMEOUT: # the subprocess has limited time to complete its job io_loop.add_timeout(datetime.timedelta(seconds=0.5), poll_media_list_process) read_media_list() - else: # process did not finish in time + else: # process did not finish in time logging.error('timeout waiting for the media listing process to finish') try: os.kill(_timelapse_process.pid, signal.SIGTERM) except: - pass # nevermind + pass # nevermind _timelapse_process.progress = -1 - else: # finished + else: # finished read_media_list() logging.debug('media listing process has returned %(count)s files' % {'count': len(media_list)}) @@ -572,11 +572,11 @@ def make_timelapse_movie(camera_config, framerate, interval, group): selected = [] for i in xrange(max_idx + 1): - slice = slices.get(i) - if not slice: + s = slices.get(i) + if not s: continue - selected.append(min(slice, key=lambda m: m['delta'])) + selected.append(min(s, key=lambda m: m['delta'])) logging.debug('selected %d/%d media files' % (len(selected), len(media_list))) @@ -585,8 +585,9 @@ def make_timelapse_movie(camera_config, framerate, interval, group): def make_movie(pictures): global _timelapse_process - cmd = 'rm -f %(tmp_filename)s;' - cmd += 'cat %(jpegs)s | ffmpeg -framerate %(framerate)s -f image2pipe -vcodec mjpeg -i - -vcodec %(codec)s -format %(format)s -b:v %(bitrate)s -qscale:v 0.1 -f avi %(tmp_filename)s' + cmd = 'rm -f %(tmp_filename)s;' + cmd += 'cat %(jpegs)s | ffmpeg -framerate %(framerate)s -f image2pipe -vcodec mjpeg -i - -vcodec %(codec)s ' \ + '-format %(format)s -b:v %(bitrate)s -qscale:v 0.1 -f avi %(tmp_filename)s' bitrate = 9999999 @@ -595,14 +596,14 @@ def make_timelapse_movie(camera_config, framerate, interval, group): 'jpegs': ' '.join((('"' + p['path'] + '"') for p in pictures)), 'framerate': framerate, 'codec': codec, - 'format': format, + 'format': fmt, 'bitrate': bitrate } logging.debug('executing "%s"' % cmd) _timelapse_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) - _timelapse_process.progress = 0.01 # 1% + _timelapse_process.progress = 0.01 # 1% # make subprocess stdout pipe non-blocking fd = _timelapse_process.stdout.fileno() @@ -616,7 +617,7 @@ def make_timelapse_movie(camera_config, framerate, interval, group): global _timelapse_data io_loop = IOLoop.instance() - if _timelapse_process.poll() is None: # not finished yet + if _timelapse_process.poll() is None: # not finished yet io_loop.add_timeout(datetime.timedelta(seconds=0.5), functools.partial(poll_movie_process, pictures)) try: @@ -640,7 +641,7 @@ def make_timelapse_movie(camera_config, framerate, interval, group): logging.debug('timelapse progress: %s' % int(100 * _timelapse_process.progress)) - else: # finished + else: # finished exit_code = _timelapse_process.poll() _timelapse_process = None @@ -761,7 +762,7 @@ def del_media_content(camera_config, path, media_type): listing = os.listdir(dir_path) thumbs = [l for l in listing if l.endswith('.thumb')] - if len(listing) == len(thumbs): # only thumbs + if len(listing) == len(thumbs): # only thumbs for p in thumbs: os.remove(os.path.join(dir_path, p)) @@ -780,7 +781,7 @@ def del_media_group(camera_config, group, media_type): if media_type == 'picture': exts = _PICTURE_EXTS - elif media_type == 'movie': + else: # media_type == 'movie' exts = _MOVIE_EXTS target_dir = camera_config.get('target_dir') @@ -804,7 +805,7 @@ def del_media_group(camera_config, group, media_type): listing = os.listdir(full_path) thumbs = [l for l in listing if l.endswith('.thumb')] - if len(listing) == len(thumbs): # only thumbs + if len(listing) == len(thumbs): # only thumbs for p in thumbs: os.remove(os.path.join(full_path, p)) @@ -822,14 +823,14 @@ def get_current_picture(camera_config, width, height): return None if width is height is None: - return jpg # no server-side resize needed + return jpg # no server-side resize needed sio = StringIO.StringIO(jpg) image = Image.open(sio) - if width and width < 1: # given as percent + if width and width < 1: # given as percent width = int(width * image.size[0]) - if height and height < 1: # given as percent + if height and height < 1: # given as percent height = int(height * image.size[1]) width = width and int(width) or image.size[0] @@ -843,7 +844,7 @@ def get_current_picture(camera_config, width, height): height = min(max_height, height) if width >= image.size[0] and height >= image.size[1]: - return jpg # no enlarging of the picture on the server side + return jpg # no enlarging of the picture on the server side image.thumbnail((width, height), Image.CUBIC) @@ -869,7 +870,7 @@ def set_prepared_cache(data): if _prepared_files.pop(key, None) is not None: logging.warn('key "%s" was still present in the prepared cache, removed' % key) - timeout = 3600 # the user has 1 hour to download the file after creation + timeout = 3600 # the user has 1 hour to download the file after creation io_loop = IOLoop.instance() io_loop.add_timeout(datetime.timedelta(seconds=timeout), clear) diff --git a/motioneye/meyectl.py b/motioneye/meyectl.py index 22838b5..64a445a 100755 --- a/motioneye/meyectl.py +++ b/motioneye/meyectl.py @@ -35,12 +35,12 @@ def find_command(command): if command == 'relayevent': relayevent_sh = os.path.join(os.path.dirname(__file__), 'scripts/relayevent.sh') - cmd = relayevent_sh + ' "%s"' % (settings._config_file or '') + cmd = relayevent_sh + ' "%s"' % (settings.config_file or '') else: cmd = __file__ cmd = sys.executable + ' ' + cmd - cmd = cmd.replace('-b', '') # remove server-specific options + cmd = cmd.replace('-b', '') # remove server-specific options cmd += ' %s ' % command cmd += ' '.join([pipes.quote(arg) for arg in sys.argv[2:] if arg not in ['-b']]) @@ -134,7 +134,7 @@ def load_settings(): # use the config file directory as base dir # if not specified otherwise in the config file base_dir = os.path.dirname(config_file) - settings._config_file = config_file + settings.config_file = config_file if not conf_path_given[0]: settings.CONF_PATH = base_dir @@ -157,10 +157,10 @@ def load_settings(): def configure_logging(cmd, log_to_file=False): if log_to_file or cmd != 'motioneye': - format = '%(asctime)s: [{cmd}] %(levelname)8s: %(message)s'.format(cmd=cmd) + fmt = '%(asctime)s: [{cmd}] %(levelname)8s: %(message)s'.format(cmd=cmd) else: - format = '%(levelname)8s: %(message)s'.format(cmd=cmd) + fmt = '%(levelname)8s: %(message)s'.format(cmd=cmd) for h in logging.getLogger().handlers: logging.getLogger().removeHandler(h) @@ -173,7 +173,7 @@ def configure_logging(cmd, log_to_file=False): log_file = None logging.basicConfig(filename=log_file, level=settings.LOG_LEVEL, - format=format, datefmt='%Y-%m-%d %H:%M:%S') + format=fmt, datefmt='%Y-%m-%d %H:%M:%S') except Exception as e: sys.stderr.write('failed to configure logging: %s\n' % e) diff --git a/motioneye/mjpgclient.py b/motioneye/mjpgclient.py index dc9c656..f3fc8b0 100644 --- a/motioneye/mjpgclient.py +++ b/motioneye/mjpgclient.py @@ -34,8 +34,8 @@ import utils class MjpgClient(IOStream): _FPS_LEN = 4 - clients = {} # dictionary of clients indexed by camera id - _last_erroneous_close_time = 0 # helps detecting erroneous connections and restart motion + clients = {} # dictionary of clients indexed by camera id + _last_erroneous_close_time = 0 # helps detecting erroneous connections and restart motion def __init__(self, camera_id, port, username, password, auth_mode): self._camera_id = camera_id @@ -45,7 +45,7 @@ class MjpgClient(IOStream): self._auth_mode = auth_mode self._auth_digest_state = {} - self._last_access = None + self._last_access = 0 self._last_jpg = None self._last_jpg_times = [] @@ -54,8 +54,11 @@ class MjpgClient(IOStream): self.set_close_callback(self.on_close) - def connect(self): + def do_connect(self): IOStream.connect(self, ('localhost', self._port), self._on_connect) + + def get_port(self): + return self._port def on_close(self): logging.debug('connection closed for mjpg client for camera %(camera_id)s on port %(port)s' % { @@ -68,11 +71,13 @@ class MjpgClient(IOStream): if getattr(self, 'error', None) and self.error.errno != errno.ECONNREFUSED: now = time.time() if now - MjpgClient._last_erroneous_close_time < settings.MJPG_CLIENT_TIMEOUT: - logging.error('connection problem detected for mjpg client for camera %(camera_id)s on port %(port)s' % { - 'port': self._port, 'camera_id': self._camera_id}) + msg = 'connection problem detected for mjpg client for camera %(camera_id)s on port %(port)s' % { + 'port': self._port, 'camera_id': self._camera_id} + + logging.error(msg) if settings.MOTION_RESTART_ON_ERRORS: - motionctl.stop(invalidate=True) # this will close all the mjpg clients + motionctl.stop(invalidate=True) # this will close all the mjpg clients motionctl.start(deferred=True) MjpgClient._last_erroneous_close_time = now @@ -80,7 +85,16 @@ class MjpgClient(IOStream): def get_last_jpg(self): self._last_access = time.time() return self._last_jpg - + + def get_last_access(self): + return self._last_access + + def get_last_jpg_time(self): + if not self._last_jpg_times: + self._last_jpg_times.append(time.time()) + + return self._last_jpg_times[-1] + def get_fps(self): if len(self._last_jpg_times) < self._FPS_LEN: return 0 # not enough "samples" @@ -100,7 +114,7 @@ class MjpgClient(IOStream): return True error = getattr(self, 'error', None) - if (error is None) or (getattr(error, 'errno', None) == 0): # error could also be ESUCCESS for some reason + if (error is None) or (getattr(error, 'errno', None) == 0): # error could also be ESUCCESS for some reason return False self._error(error) @@ -127,7 +141,7 @@ class MjpgClient(IOStream): auth_header = utils.build_basic_header(self._username, self._password) self.write('GET / HTTP/1.0\r\n\r\nAuthorization: %s\r\n\r\n' % auth_header) - else: # in digest auth mode, the header is built upon receiving 401 + else: # in digest auth mode, the header is built upon receiving 401 self.write('GET / HTTP/1.0\r\n\r\n') self._seek_http() @@ -142,7 +156,7 @@ class MjpgClient(IOStream): if data.endswith('401 '): self._seek_www_authenticate() - else: # no authorization required, skip to content length + else: # no authorization required, skip to content length self._seek_content_length() def _seek_www_authenticate(self): @@ -252,7 +266,7 @@ def get_jpg(camera_id): auth_mode = 'digest' if camera_config.get('stream_auth_method') > 1 else 'basic' client = MjpgClient(camera_id, port, username, password, auth_mode) - client.connect() + client.do_connect() MjpgClient.clients[camera_id] = client @@ -284,37 +298,33 @@ def _garbage_collector(): now = time.time() for camera_id, client in MjpgClient.clients.items(): - port = client._port - - # check for jpeg frame timeout - if not client._last_jpg_times: - client._last_jpg_times.append(now) - - last_jpg_time = client._last_jpg_times[-1] + port = client.get_port() if client.closed(): continue + # check for jpeg frame timeout + last_jpg_time = client.get_last_jpg_time() delta = now - last_jpg_time if delta > settings.MJPG_CLIENT_TIMEOUT: logging.error('mjpg client timed out receiving data for camera %(camera_id)s on port %(port)s' % { 'camera_id': camera_id, 'port': port}) if settings.MOTION_RESTART_ON_ERRORS: - motionctl.stop(invalidate=True) # this will close all the mjpg clients + motionctl.stop(invalidate=True) # this will close all the mjpg clients motionctl.start(deferred=True) break # check for last access timeout - if client._last_access is None: - continue - - delta = now - client._last_access + delta = now - client.get_last_access() if settings.MJPG_CLIENT_IDLE_TIMEOUT and delta > settings.MJPG_CLIENT_IDLE_TIMEOUT: - logging.debug('mjpg client for camera %(camera_id)s on port %(port)s has been idle for %(timeout)s seconds, removing it' % { + msg = ('mjpg client for camera %(camera_id)s on port %(port)s has been idle ' + 'for %(timeout)s seconds, removing it' % { 'camera_id': camera_id, 'port': port, 'timeout': settings.MJPG_CLIENT_IDLE_TIMEOUT}) + logging.debug(msg) + client.close() continue diff --git a/motioneye/monitor.py b/motioneye/monitor.py index a3da581..6cb8738 100644 --- a/motioneye/monitor.py +++ b/motioneye/monitor.py @@ -23,29 +23,27 @@ import urllib import config -DEFAULT_INTERVAL = 1 # seconds +DEFAULT_INTERVAL = 1 # seconds -_monior_info_cache_by_camera_id = {} +_monitor_info_cache_by_camera_id = {} _last_call_time_by_camera_id = {} _interval_by_camera_id = {} def get_monitor_info(camera_id): - global _command_cache_time - now = time.time() command = config.get_monitor_command(camera_id) if command is None: return '' - monitor_info = _monior_info_cache_by_camera_id.get(camera_id) + monitor_info = _monitor_info_cache_by_camera_id.get(camera_id) last_call_time = _last_call_time_by_camera_id.get(camera_id, 0) interval = _interval_by_camera_id.get(camera_id, DEFAULT_INTERVAL) if monitor_info is None or now - last_call_time > interval: monitor_info, interval = _exec_monitor_command(command) monitor_info = urllib.quote(monitor_info, safe='') _interval_by_camera_id[camera_id] = interval - _monior_info_cache_by_camera_id[camera_id] = monitor_info + _monitor_info_cache_by_camera_id[camera_id] = monitor_info _last_call_time_by_camera_id[camera_id] = now return monitor_info diff --git a/motioneye/motionctl.py b/motioneye/motionctl.py index 832ed38..cc4611b 100644 --- a/motioneye/motionctl.py +++ b/motioneye/motionctl.py @@ -52,17 +52,17 @@ def find_motion(): else: return None, None - else: # autodetect motion binary path + else: # autodetect motion binary path try: binary = subprocess.check_output(['which', 'motion'], stderr=utils.DEV_NULL).strip() - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None, None try: help = subprocess.check_output(binary + ' -h || true', shell=True) - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None, None result = re.findall('motion Version ([^,]+)', help, re.IGNORECASE) @@ -105,11 +105,8 @@ def start(deferred=False): motion_log_path = os.path.join(settings.LOG_PATH, 'motion.log') motion_pid_path = os.path.join(settings.RUN_PATH, 'motion.pid') - args = [program, - '-n', - '-c', motion_config_path] - - args.append('-d') + args = [program, '-n', '-c', motion_config_path, '-d'] + if settings.LOG_LEVEL <= logging.DEBUG: args.append('9') @@ -119,7 +116,7 @@ def start(deferred=False): elif settings.LOG_LEVEL <= logging.ERROR: args.append('4') - else: # fatat, quiet + else: # fatal, quiet args.append('1') log_file = open(motion_log_path, 'w') @@ -331,14 +328,14 @@ def has_old_config_format(): if not binary: return False - if version.startswith('trunkREV'): # e.g. "trunkREV599" + if version.startswith('trunkREV'): # e.g. "trunkREV599" version = int(version[8:]) return version <= _LAST_OLD_CONFIG_VERSIONS[0] - elif version.lower().count('git'): # e.g. "Unofficial-Git-a5b5f13" or "3.2.12+git20150927mrdave" - return False # all git versions are assumed to be new + elif version.lower().count('git'): # e.g. "Unofficial-Git-a5b5f13" or "3.2.12+git20150927mrdave" + return False # all git versions are assumed to be new - else: # stable release, should have the format "x.y.z" + else: # stable release, should have the format "x.y.z" return update.compare_versions(version, _LAST_OLD_CONFIG_VERSIONS[1]) <= 0 @@ -359,15 +356,15 @@ def get_rtsp_support(): if not binary: return [] - if version.startswith('trunkREV'): # e.g. trunkREV599 + if version.startswith('trunkREV'): # e.g. trunkREV599 version = int(version[8:]) if version > _LAST_OLD_CONFIG_VERSIONS[0]: return ['tcp'] elif version.lower().count('git') or update.compare_versions(version, '3.4') >= 0: - return ['tcp', 'udp'] # all git versions are assumed to support both transport protocols + return ['tcp', 'udp'] # all git versions are assumed to support both transport protocols - else: # stable release, should be in the format x.y.z + else: # stable release, should be in the format x.y.z return [] diff --git a/motioneye/ordereddict.py b/motioneye/ordereddict.py deleted file mode 100644 index 0874135..0000000 --- a/motioneye/ordereddict.py +++ /dev/null @@ -1,258 +0,0 @@ -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): #@NoSelf - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) diff --git a/motioneye/powerctl.py b/motioneye/powerctl.py index 4171fbd..2c9149e 100644 --- a/motioneye/powerctl.py +++ b/motioneye/powerctl.py @@ -25,7 +25,7 @@ def _find_prog(prog): try: return subprocess.check_output(['which', prog], stderr=utils.DEV_NULL).strip() - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None diff --git a/motioneye/prefs.py b/motioneye/prefs.py index 0b3a8c1..386dc66 100644 --- a/motioneye/prefs.py +++ b/motioneye/prefs.py @@ -45,7 +45,7 @@ def _load(): logging.debug('loading preferences from "%s"...' % file_path) try: - file = open(file_path, 'r') + f = open(file_path, 'r') except Exception as e: logging.error('could not open preferences file "%s": %s' % (file_path, e)) @@ -53,13 +53,13 @@ def _load(): return try: - _prefs = json.load(file) + _prefs = json.load(f) except Exception as e: - logging.error('could not read preferences from file "%s": %s'(file_path, e)) + logging.error('could not read preferences from file "%s": %s' % (file_path, e)) finally: - file.close() + f.close() else: logging.debug('preferences file "%s" does not exist, using default preferences' % file_path) @@ -71,7 +71,7 @@ def _save(): logging.debug('saving preferences to "%s"...' % file_path) try: - file = open(file_path, 'w') + f = open(file_path, 'w') except Exception as e: logging.error('could not open preferences file "%s": %s' % (file_path, e)) @@ -79,13 +79,13 @@ def _save(): return try: - json.dump(_prefs, file, sort_keys=True, indent=4) + json.dump(_prefs, f, sort_keys=True, indent=4) except Exception as e: - logging.error('could not save preferences to file "%s": %s'(file_path, e)) + logging.error('could not save preferences to file "%s": %s' % (file_path, e)) finally: - file.close() + f.close() def get(username, key=None): diff --git a/motioneye/remote.py b/motioneye/remote.py index 8e6e455..b5cb997 100644 --- a/motioneye/remote.py +++ b/motioneye/remote.py @@ -28,7 +28,9 @@ import utils _DOUBLE_SLASH_REGEX = re.compile('//+') -def _make_request(scheme, host, port, username, password, path, method='GET', data=None, query=None, timeout=None, content_type=None): +def _make_request(scheme, host, port, username, password, path, method='GET', data=None, query=None, + timeout=None, content_type=None): + path = _DOUBLE_SLASH_REGEX.sub('/', path) url = '%(scheme)s://%(host)s%(port)s%(path)s' % { 'scheme': scheme, @@ -38,7 +40,7 @@ def _make_request(scheme, host, port, username, password, path, method='GET', da query = dict(query or {}) query['_username'] = username or '' - query['_admin'] = 'true' # always use the admin account + query['_admin'] = 'true' # always use the admin account if url.count('?'): url += '&' @@ -57,7 +59,7 @@ def _make_request(scheme, host, port, username, password, path, method='GET', da headers['Content-Type'] = content_type return HTTPRequest(url, method, body=data, connect_timeout=timeout, request_timeout=timeout, headers=headers, - validate_cert=settings.VALIDATE_CERTS) + validate_cert=settings.VALIDATE_CERTS) def _callback_wrapper(callback): @@ -131,7 +133,7 @@ def list(local_config, callback): 'url': pretty_camera_url(local_config, camera=False)}) request = _make_request(scheme, host, port, username, password, - path + '/config/list/') + path + '/config/list/') def on_response(response): def make_camera_response(c): @@ -160,8 +162,7 @@ def list(local_config, callback): cameras = response['cameras'] # filter out simple mjpeg cameras - cameras = [make_camera_response(c) for c in cameras - if c['proto'] != 'mjpeg' and c.get('enabled')] + cameras = [make_camera_response(c) for c in cameras if c['proto'] != 'mjpeg' and c.get('enabled')] callback(cameras) @@ -177,7 +178,7 @@ def get_config(local_config, callback): 'url': pretty_camera_url(local_config)}) request = _make_request(scheme, host, port, username, password, - path + '/config/%(id)s/get/' % {'id': camera_id}) + path + '/config/%(id)s/get/' % {'id': camera_id}) def on_response(response): if response.error: @@ -221,10 +222,10 @@ def set_config(local_config, ui_config, callback): 'url': pretty_camera_url(local_config)}) ui_config = json.dumps(ui_config) - - request = _make_request(scheme, host, port, username, password, - path + '/config/%(id)s/set/' % {'id': camera_id}, - method='POST', data=ui_config, content_type='application/json') + + p = path + '/config/%(id)s/set/' % {'id': camera_id} + request = _make_request(scheme, host, port, username, password, p, + method='POST', data=ui_config, content_type='application/json') def on_response(response): if response.error: @@ -249,10 +250,10 @@ def set_preview(local_config, controls, callback): 'url': pretty_camera_url(local_config)}) data = json.dumps(controls) - - request = _make_request(scheme, host, port, username, password, - path + '/config/%(id)s/set_preview/' % {'id': camera_id}, - method='POST', data=data, content_type='application/json') + + p = path + '/config/%(id)s/set_preview/' % {'id': camera_id} + request = _make_request(scheme, host, port, username, password, p, + method='POST', data=data, content_type='application/json') def on_response(response): if response.error: @@ -279,9 +280,9 @@ def test(local_config, data, callback): data = json.dumps(data) - request = _make_request(scheme, host, port, username, password, - path + '/config/%(id)s/test/' % {'id': camera_id}, - method='POST', data=data, content_type='application/json') + p = path + '/config/%(id)s/test/' % {'id': camera_id} + request = _make_request(scheme, host, port, username, password, p, + method='POST', data=data, content_type='application/json') def on_response(response): if response.error: @@ -313,10 +314,9 @@ def get_current_picture(local_config, width, height, callback): if height: query['height'] = str(height) - - request = _make_request(scheme, host, port, username, password, - path + '/picture/%(id)s/current/' % {'id': camera_id}, - query=query) + + p = path + '/picture/%(id)s/current/' % {'id': camera_id} + request = _make_request(scheme, host, port, username, password, p, query=query) def on_response(response): cookies = utils.parse_cookies(response.headers.get_list('Set-Cookie')) @@ -351,10 +351,9 @@ def list_media(local_config, media_type, prefix, callback): query['prefix'] = prefix # timeout here is 10 times larger than usual - we expect a big delay when fetching the media list - request = _make_request(scheme, host, port, username, password, - path + '/%(media_type)s/%(id)s/list/' % { - 'id': camera_id, 'media_type': media_type}, query=query, - timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) + p = path + '/%(media_type)s/%(id)s/list/' % {'id': camera_id, 'media_type': media_type} + request = _make_request(scheme, host, port, username, password, p, query=query, + timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: @@ -396,7 +395,7 @@ def get_media_content(local_config, filename, media_type, callback): # timeout here is 10 times larger than usual - we expect a big delay when fetching the media list request = _make_request(scheme, host, port, username, password, - path, timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) + path, timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: @@ -429,15 +428,18 @@ def make_zipped_content(local_config, media_type, group, callback): # timeout here is 100 times larger than usual - we expect a big delay request = _make_request(scheme, host, port, username, password, - prepare_path, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT) + prepare_path, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: - logging.error('failed to prepare zip file for group "%(group)s" of remote camera %(id)s on %(url)s: %(msg)s' % { + msg = 'failed to prepare zip file for group "%(group)s" ' \ + 'of remote camera %(id)s on %(url)s: %(msg)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config), - 'msg': utils.pretty_http_error(response)}) + 'msg': utils.pretty_http_error(response)} + + logging.error(msg) return callback(error=utils.pretty_http_error(response)) @@ -463,14 +465,15 @@ def get_zipped_content(local_config, media_type, key, group, callback): logging.debug('downloading zip file for remote camera %(id)s on %(url)s' % { 'id': camera_id, 'url': pretty_camera_url(local_config)}) - - request = _make_request(scheme, host, port, username, password, - path + '/%(media_type)s/%(id)s/zipped/%(group)s/?key=%(key)s' % { - 'media_type': media_type, - 'group': group, - 'id': camera_id, - 'key': key}, - timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) + + p = path + '/%(media_type)s/%(id)s/zipped/%(group)s/?key=%(key)s' % { + 'media_type': media_type, + 'group': group, + 'id': camera_id, + 'key': key} + + request = _make_request(scheme, host, port, username, password, p, + timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: @@ -494,12 +497,15 @@ def get_zipped_content(local_config, media_type, key, group, callback): def make_timelapse_movie(local_config, framerate, interval, group, callback): scheme, host, port, username, password, path, camera_id = _remote_params(local_config) - logging.debug('making timelapse movie for group "%(group)s" of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s' % { + msg = 'making timelapse movie for group "%(group)s" of remote camera %(id)s ' \ + 'with rate %(framerate)s/%(int)s on %(url)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'framerate': framerate, 'int': interval, - 'url': pretty_camera_url(local_config)}) + 'url': pretty_camera_url(local_config)} + + logging.debug(msg) path += '/picture/%(id)s/timelapse/%(group)s/?interval=%(int)s&framerate=%(framerate)s' % { 'id': camera_id, @@ -508,17 +514,20 @@ def make_timelapse_movie(local_config, framerate, interval, group, callback): 'group': group} request = _make_request(scheme, host, port, username, password, - path, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT) + path, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: - logging.error('failed to make timelapse movie for group "%(group)s" of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s: %(msg)s' % { + msg = 'failed to make timelapse movie for group "%(group)s" of remote camera %(id)s ' \ + 'with rate %(framerate)s/%(int)s on %(url)s: %(msg)s' % { 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config), 'int': interval, 'framerate': framerate, - 'msg': utils.pretty_http_error(response)}) + 'msg': utils.pretty_http_error(response)} + + logging.error(msg) return callback(error=utils.pretty_http_error(response)) @@ -544,11 +553,11 @@ def check_timelapse_movie(local_config, group, callback): logging.debug('checking timelapse movie status for remote camera %(id)s on %(url)s' % { 'id': camera_id, 'url': pretty_camera_url(local_config)}) - - request = _make_request(scheme, host, port, username, password, - path + '/picture/%(id)s/timelapse/%(group)s/?check=true' % { - 'id': camera_id, - 'group': group}) + + p = path + '/picture/%(id)s/timelapse/%(group)s/?check=true' % { + 'id': camera_id, + 'group': group} + request = _make_request(scheme, host, port, username, password, p) def on_response(response): if response.error: @@ -581,13 +590,14 @@ def get_timelapse_movie(local_config, key, group, callback): logging.debug('downloading timelapse movie for remote camera %(id)s on %(url)s' % { 'id': camera_id, 'url': pretty_camera_url(local_config)}) - - request = _make_request(scheme, host, port, username, password, - path + '/picture/%(id)s/timelapse/%(group)s/?key=%(key)s' % { - 'id': camera_id, - 'group': group, - 'key': key}, - timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) + + p = path + '/picture/%(id)s/timelapse/%(group)s/?key=%(key)s' % { + 'id': camera_id, + 'group': group, + 'key': key} + + request = _make_request(scheme, host, port, username, password, p, + timeout=10 * settings.REMOTE_REQUEST_TIMEOUT) def on_response(response): if response.error: @@ -629,8 +639,7 @@ def get_media_preview(local_config, filename, media_type, width, height, callbac if height: query['height'] = str(height) - request = _make_request(scheme, host, port, username, password, - path, query=query) + request = _make_request(scheme, host, port, username, password, path, query=query) def on_response(response): if response.error: @@ -661,9 +670,8 @@ def del_media_content(local_config, filename, media_type, callback): 'id': camera_id, 'filename': filename} - request = _make_request(scheme, host, port, username, password, - path, method='POST', data='{}', - timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') + request = _make_request(scheme, host, port, username, password, path, method='POST', data='{}', + timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') def on_response(response): if response.error: @@ -694,9 +702,8 @@ def del_media_group(local_config, group, media_type, callback): 'id': camera_id, 'group': group} - request = _make_request(scheme, host, port, username, password, path, - method='POST', data='{}', - timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') + request = _make_request(scheme, host, port, username, password, path, method='POST', data='{}', + timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') def on_response(response): if response.error: @@ -726,9 +733,8 @@ def exec_action(local_config, action, callback): 'action': action, 'id': camera_id} - request = _make_request(scheme, host, port, username, password, path, - method='POST', data='{}', - timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') + request = _make_request(scheme, host, port, username, password, path, method='POST', data='{}', + timeout=settings.REMOTE_REQUEST_TIMEOUT, content_type='application/json') def on_response(response): if response.error: diff --git a/motioneye/sendmail.py b/motioneye/sendmail.py index 130ef94..664e547 100644 --- a/motioneye/sendmail.py +++ b/motioneye/sendmail.py @@ -64,13 +64,13 @@ def send_mail(server, port, account, password, tls, _from, to, subject, message, email['Date'] = formatdate(localtime=True) email.attach(MIMEText(message)) - for file in reversed(files): + for f in reversed(files): part = MIMEBase('image', 'jpeg') - with open(file, 'rb') as f: + with open(f, 'rb') as f: part.set_payload(f.read()) Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file)) + part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f)) email.attach(part) if files: @@ -94,7 +94,7 @@ def make_message(subject, message, camera_id, moment, timespan, callback): if media_files: logging.debug('got media files') - media_files = [m for m in media_files if abs(m['timestamp'] - timestamp) < timespan] # filter out non-recent media files + media_files = [m for m in media_files if abs(m['timestamp'] - timestamp) < timespan] # filter out non-recent media files media_files.sort(key=lambda m: m['timestamp'], reverse=True) media_files = [os.path.join(camera_config['target_dir'], re.sub('^/', '', m['path'])) for m in media_files] @@ -125,7 +125,7 @@ def make_message(subject, message, camera_id, moment, timespan, callback): return on_media_files([]) logging.debug('waiting for pictures to be taken') - time.sleep(timespan) # give motion some time to create motion pictures + time.sleep(timespan) # give motion some time to create motion pictures logging.debug('creating email message') mediafiles.list_media(camera_config, media_type='picture', callback=on_media_files) @@ -155,7 +155,7 @@ def main(parser, args): # the motion daemon overrides SIGCHLD, # so we must restore it here, # or otherwise media listing won't work - signal.signal(signal.SIGCHLD,signal.SIG_DFL) + signal.signal(signal.SIGCHLD, signal.SIG_DFL) if len(args) == 12: # backwards compatibility with older configs lacking "from" field @@ -177,7 +177,7 @@ def main(parser, args): message = messages.get(options.msg_id) subject = subjects.get(options.msg_id) options.moment = datetime.datetime.strptime(options.moment, '%Y-%m-%dT%H:%M:%S') - options.password = options.password.replace('\\;', ';') # unescape password + options.password = options.password.replace('\\;', ';') # unescape password # do not wait too long for media list, # email notifications are critical @@ -207,7 +207,7 @@ def main(parser, args): def on_message(subject, message, files): try: send_mail(options.server, options.port, options.account, options.password, - options.tls, _from, to, subject, message, files or []) + options.tls, _from, to, subject, message, files or []) logging.info('email sent') except Exception as e: diff --git a/motioneye/server.py b/motioneye/server.py index 12ea1be..7ff1313 100644 --- a/motioneye/server.py +++ b/motioneye/server.py @@ -45,7 +45,7 @@ class Daemon(object): def daemonize(self): # first fork try: - if os.fork() > 0: # parent + if os.fork() > 0: # parent sys.exit(0) except OSError, e: @@ -58,7 +58,7 @@ class Daemon(object): # second fork try: - if os.fork() > 0: # parent + if os.fork() > 0: # parent sys.exit(0) except OSError, e: @@ -291,7 +291,7 @@ def test_requirements(): def make_media_folders(): import config - config.get_main() # just to have main config already loaded + config.get_main() # just to have main config already loaded camera_ids = config.get_camera_ids() for camera_id in camera_ids: @@ -339,7 +339,7 @@ def start_motion(): def parse_options(parser, args): parser.add_argument('-b', help='start the server in background (daemonize)', - action='store_true', dest='background', default=False) + action='store_true', dest='background', default=False) return parser.parse_args(args) @@ -388,7 +388,7 @@ def run(): template.add_context('static_path', 'static/') application = Application(handler_mapping, debug=False, log_function=_log_request, - static_path=settings.STATIC_PATH, static_url_prefix='/static/') + static_path=settings.STATIC_PATH, static_url_prefix='/static/') application.listen(settings.PORT, settings.LISTEN) logging.info('server started') diff --git a/motioneye/settings.py b/motioneye/settings.py index cfd8488..4478661 100644 --- a/motioneye/settings.py +++ b/motioneye/settings.py @@ -5,7 +5,7 @@ import sys import motioneye -_config_file = None +config_file = None # the root directory of the project PROJECT_PATH = os.path.dirname(motioneye.__file__) @@ -17,7 +17,7 @@ TEMPLATE_PATH = os.path.join(PROJECT_PATH, 'templates') STATIC_PATH = os.path.join(PROJECT_PATH, 'static') # path to the configuration directory (must be writable by motionEye) -CONF_PATH = [sys.prefix, ''][sys.prefix == '/usr'] + '/etc/motioneye' +CONF_PATH = [sys.prefix, ''][sys.prefix == '/usr'] + '/etc/motioneye' # path to the directory where pid files go (must be writable by motionEye) for d in ['/run', '/var/run', '/tmp', '/var/tmp']: diff --git a/motioneye/smbctl.py b/motioneye/smbctl.py index 894fbe5..4a7d48b 100644 --- a/motioneye/smbctl.py +++ b/motioneye/smbctl.py @@ -42,7 +42,7 @@ def find_mount_cifs(): try: return subprocess.check_output(['which', 'mount.cifs'], stderr=utils.DEV_NULL).strip() - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None @@ -92,7 +92,7 @@ def list_mounts(): continue server, share = match.groups() - share = share.replace('\\040', ' ') # spaces are reported oddly by /proc/mounts + share = share.replace('\\040', ' ') # spaces are reported oddly by /proc/mounts match = re.search('username=([a-z][-\w]*)', opts) if match: @@ -119,16 +119,18 @@ def update_mounts(): mounts = list_mounts() mounts = dict(((m['server'], m['share'], m['username'] or ''), False) for m in mounts) - should_stop = False # indicates that motion should be stopped immediately - should_start = True # indicates that motion can be started afterwards + should_stop = False # indicates that motion should be stopped immediately + should_start = True # indicates that motion can be started afterwards for network_share in network_shares: key = (network_share['server'], network_share['share'], network_share['username'] or '') - if key in mounts: # found + if key in mounts: # found mounts[key] = True - else: # needs to be mounted + else: # needs to be mounted should_stop = True - if not _mount(network_share['server'], network_share['share'], network_share['username'], network_share['password']): + if not _mount(network_share['server'], network_share['share'], + network_share['username'], network_share['password']): + should_start = False # unmount the no longer necessary mounts @@ -137,7 +139,7 @@ def update_mounts(): _umount(server, share, username) should_stop = True - return (should_stop, should_start) + return should_stop, should_start def test_share(server, share, username, password, root_directory): diff --git a/motioneye/tasks.py b/motioneye/tasks.py index d6c412b..996b958 100644 --- a/motioneye/tasks.py +++ b/motioneye/tasks.py @@ -59,7 +59,6 @@ def start(): def stop(): global _pool - #_pool.terminate() _pool = None @@ -69,7 +68,7 @@ def add(when, func, tag=None, callback=None, **params): now = time.time() - if isinstance(when, int): # delay, in seconds + if isinstance(when, int): # delay, in seconds when += now elif isinstance(when, datetime.timedelta): @@ -117,7 +116,7 @@ def _load(): logging.debug('loading tasks from "%s"...' % file_path) try: - file = open(file_path, 'r') + f = open(file_path, 'r') except Exception as e: logging.error('could not open tasks file "%s": %s' % (file_path, e)) @@ -125,13 +124,13 @@ def _load(): return try: - _tasks = cPickle.load(file) + _tasks = cPickle.load(f) except Exception as e: logging.error('could not read tasks from file "%s": %s' % (file_path, e)) finally: - file.close() + f.close() def _save(): @@ -140,7 +139,7 @@ def _save(): logging.debug('saving tasks to "%s"...' % file_path) try: - file = open(file_path, 'w') + f = open(file_path, 'w') except Exception as e: logging.error('could not open tasks file "%s": %s' % (file_path, e)) @@ -150,10 +149,10 @@ def _save(): try: # don't save tasks that have a callback tasks = [t for t in _tasks if not t[3]] - cPickle.dump(tasks, file) + cPickle.dump(tasks, f) except Exception as e: - logging.error('could not save tasks to file "%s": %s'% (file_path, e)) + logging.error('could not save tasks to file "%s": %s' % (file_path, e)) finally: - file.close() + f.close() diff --git a/motioneye/tzctl.py b/motioneye/tzctl.py index a757273..e85710f 100644 --- a/motioneye/tzctl.py +++ b/motioneye/tzctl.py @@ -32,24 +32,24 @@ def get_time_zone(): def _get_time_zone_symlink(): - file = settings.LOCAL_TIME_FILE - if not file: + f = settings.LOCAL_TIME_FILE + if not f: return None - for i in xrange(8): # recursively follow the symlinks @UnusedVariable + for i in xrange(8): # recursively follow the symlinks @UnusedVariable try: - file = os.readlink(file) + f = os.readlink(f) except OSError: break - if file and file.startswith('/usr/share/zoneinfo/'): - file = file[20:] + if f and f.startswith('/usr/share/zoneinfo/'): + f = f[20:] else: - file = None + f = None - time_zone = file or None + time_zone = f or None if time_zone: logging.debug('found time zone by symlink method: %s' % time_zone) @@ -105,7 +105,7 @@ def _set_time_zone(time_zone): os.remove(settings.LOCAL_TIME_FILE) except: - pass # nevermind + pass # nevermind try: os.symlink(zoneinfo_file, settings.LOCAL_TIME_FILE) diff --git a/motioneye/update.py b/motioneye/update.py index a3ceacd..ff8e688 100644 --- a/motioneye/update.py +++ b/motioneye/update.py @@ -56,7 +56,7 @@ def _get_os_version_uname(): return name, version except: - return ('Linux', '') # most likely :) + return 'Linux', '' # most likely :) def compare_versions(version1, version2): diff --git a/motioneye/uploadservices.py b/motioneye/uploadservices.py index b76abc9..fd93d14 100644 --- a/motioneye/uploadservices.py +++ b/motioneye/uploadservices.py @@ -75,7 +75,9 @@ class UploadService(object): raise Exception(msg) if st.st_size > self.MAX_FILE_SIZE: - msg = 'file "%s" is too large (%sMB/%sMB)' % (filename, st.st_size / 1024 / 1024, self.MAX_FILE_SIZE / 1024 / 1024) + msg = 'file "%s" is too large (%sMB/%sMB)' % \ + (filename, st.st_size / 1024 / 1024, self.MAX_FILE_SIZE / 1024 / 1024) + self.error(msg) raise Exception(msg) @@ -348,7 +350,7 @@ class GoogleDrive(UploadService): # retry the request with refreshed credentials return self._request(url, body, headers, retry_auth=False) - except Exception as e: + except Exception: self.error('refreshing credentials failed') raise @@ -555,9 +557,9 @@ class Dropbox(UploadService): self.save() # retry the request with refreshed credentials - self._request(url, body, headers, retry_auth=False) + return self._request(url, body, headers, retry_auth=False) - except Exception as e: + except Exception: self.error('refreshing credentials failed') raise @@ -885,7 +887,7 @@ def _load(): logging.debug('loading upload services state from "%s"...' % file_path) try: - file = open(file_path, 'r') + f = open(file_path, 'r') except Exception as e: logging.error('could not open upload services state file "%s": %s' % (file_path, e)) @@ -893,7 +895,7 @@ def _load(): return services try: - data = json.load(file) + data = json.load(f) except Exception as e: logging.error('could not read upload services state from file "%s": %s' % (file_path, e)) @@ -901,7 +903,7 @@ def _load(): return services finally: - file.close() + f.close() for camera_id, d in data.iteritems(): for name, state in d.iteritems(): @@ -929,7 +931,7 @@ def _save(services): data.setdefault(str(camera_id), {})[name] = service.dump() try: - file = open(file_path, 'w') + f = open(file_path, 'w') except Exception as e: logging.error('could not open upload services state file "%s": %s' % (file_path, e)) @@ -937,10 +939,10 @@ def _save(services): return try: - json.dump(data, file, sort_keys=True, indent=4) + json.dump(data, f, sort_keys=True, indent=4) except Exception as e: - logging.error('could not save upload services state to file "%s": %s'(file_path, e)) + logging.error('could not save upload services state to file "%s": %s' % (file_path, e)) finally: - file.close() + f.close() diff --git a/motioneye/utils.py b/motioneye/utils.py index 95cea8a..d8456e2 100644 --- a/motioneye/utils.py +++ b/motioneye/utils.py @@ -37,14 +37,8 @@ from tornado.ioloop import IOLoop import settings -try: - from collections import OrderedDict # @UnusedImport -except: - from ordereddict import OrderedDict # @UnusedImport @Reimport - - -_SIGNATURE_REGEX = re.compile('[^a-zA-Z0-9/?_.=&{}\[\]":, _-]') +_SIGNATURE_REGEX = re.compile('[^a-zA-Z0-9/?_.=&{}\[\]":, -]') _SPECIAL_COOKIE_NAMES = {'expires', 'domain', 'path', 'secure', 'httponly'} MASK_WIDTH = 32 @@ -74,9 +68,13 @@ COMMON_RESOLUTIONS = [ ] +def _(x): + return x # this could later be replaced by a proper translate function + + def pretty_date_time(date_time, tzinfo=None, short=False): if date_time is None: - return '('+ _('never') + ')' + return '(' + _('never') + ')' if isinstance(date_time, int): return pretty_date_time(datetime.datetime.fromtimestamp(date_time)) @@ -115,7 +113,7 @@ def pretty_date_time(date_time, tzinfo=None, short=False): def pretty_date(date): if date is None: - return '('+ _('never') + ')' + return '(' + _('never') + ')' if isinstance(date, int): return pretty_date(datetime.datetime.fromtimestamp(date)) @@ -212,36 +210,36 @@ def pretty_duration(duration): return special_result if days: - format = "{d}d{h}h{m}m" + fmt = "{d}d{h}h{m}m" elif hours: - format = "{h}h{m}m" + fmt = "{h}h{m}m" elif minutes: - format = "{m}m" + fmt = "{m}m" if seconds: - format += "{s}s" + fmt += "{s}s" else: - format = "{s}s" + fmt = "{s}s" if negative: - format = '-' + format + fmt = '-' + fmt - return format.format(d=days, h=hours, m=minutes, s=seconds) + return fmt.format(d=days, h=hours, m=minutes, s=seconds) def pretty_size(size): - if size < 1024: # less than 1kB + if size < 1024: # less than 1kB size, unit = size, 'B' - elif size < 1024 * 1024: # less than 1MB + elif size < 1024 * 1024: # less than 1MB size, unit = size / 1024.0, 'kB' - elif size < 1024 * 1024 * 1024: # less than 1GB + elif size < 1024 * 1024 * 1024: # less than 1GB size, unit = size / 1024.0 / 1024, 'MB' - else: # greater than or equal to 1GB + else: # greater than or equal to 1GB size, unit = size / 1024.0 / 1024 / 1024, 'GB' return '%.1f %s' % (size, unit) @@ -333,31 +331,31 @@ def get_disk_usage(path): total_size = total_blocks * block_size used_size = total_size - free_size - return (used_size, total_size) + return used_size, total_size def is_local_motion_camera(config): - '''Tells if a camera is managed by the local motion instance.''' + """Tells if a camera is managed by the local motion instance.""" return bool(config.get('videodevice') or config.get('netcam_url')) def is_remote_camera(config): - '''Tells if a camera is managed by a remote motionEye server.''' + """Tells if a camera is managed by a remote motionEye server.""" return config.get('@proto') == 'motioneye' def is_v4l2_camera(config): - '''Tells if a camera is a v4l2 device managed by the local motion instance.''' + """Tells if a camera is a v4l2 device managed by the local motion instance.""" return bool(config.get('videodevice')) def is_net_camera(config): - '''Tells if a camera is a network camera managed by the local motion instance.''' + """Tells if a camera is a network camera managed by the local motion instance.""" return bool(config.get('netcam_url')) def is_simple_mjpeg_camera(config): - '''Tells if a camera is a simple MJPEG camera not managed by any motion instance.''' + """Tells if a camera is a simple MJPEG camera not managed by any motion instance.""" return bool(config.get('@proto') == 'mjpeg') @@ -390,8 +388,9 @@ def test_mjpeg_url(data, auth_modes, allow_jpeg, callback): logging.debug('testing (m)jpg netcam at %s using %s authentication' % (url, auth)) request = HTTPRequest(url, auth_username=username, auth_password=password, auth_mode=auth_modes.pop(0), - connect_timeout=settings.REMOTE_REQUEST_TIMEOUT, request_timeout=settings.REMOTE_REQUEST_TIMEOUT, - header_callback=on_header, validate_cert=settings.VALIDATE_CERTS) + connect_timeout=settings.REMOTE_REQUEST_TIMEOUT, + request_timeout=settings.REMOTE_REQUEST_TIMEOUT, + header_callback=on_header, validate_cert=settings.VALIDATE_CERTS) http_client = AsyncHTTPClient(force_instance=True) http_client.fetch(request, on_response) @@ -474,7 +473,7 @@ def test_rtsp_url(data, callback): stream.connect((host, int(port)), on_connect) timeout[0] = io_loop.add_timeout(datetime.timedelta(seconds=settings.MJPG_CLIENT_TIMEOUT), - functools.partial(on_connect, _timeout=True)) + functools.partial(on_connect, _timeout=True)) return stream @@ -655,7 +654,7 @@ def compute_signature(method, path, body, key): key = _SIGNATURE_REGEX.sub('-', key) if body and body.startswith('---'): - body = None # file attachment + body = None # file attachment body = body and _SIGNATURE_REGEX.sub('-', body.decode('utf8')) @@ -679,6 +678,7 @@ def parse_cookies(cookies_headers): return parsed + def build_basic_header(username, password): return 'Basic ' + base64.encodestring('%s:%s' % (username, password)).replace('\n', '') @@ -705,7 +705,7 @@ def build_digest_header(method, url, username, password, state): return hashlib.md5(x).hexdigest() hash_utf8 = md5_utf8 - elif _algorithm == 'SHA': + else: # _algorithm == 'SHA' def sha_utf8(x): if isinstance(x, str): x = x.encode('utf-8') @@ -773,7 +773,7 @@ def build_digest_header(method, url, username, password, state): state['last_nonce'] = last_nonce state['nonce_count'] = nonce_count - return 'Digest %s' % (base) + return 'Digest %s' % base def urlopen(*args, **kwargs): @@ -801,24 +801,24 @@ def build_editable_mask_file(camera_id, mask_lines, capture_width=None, capture_ mask_lines = mask_lines[2:] logging.debug('building editable mask for camera with id %s (%sx%s)' % - (camera_id, width, height)) + (camera_id, width, height)) # horizontal rectangles - nx = MASK_WIDTH # number of rectangles + nx = MASK_WIDTH # number of rectangles if width % nx: nx -= 1 - rx = width % nx # remainder + rx = width % nx # remainder else: rx = 0 - rw = width / nx # rectangle width + rw = width / nx # rectangle width # vertical rectangles - ny = mask_height = height * MASK_WIDTH / width # number of rectangles + ny = mask_height = height * MASK_WIDTH / width # number of rectangles if height % ny: ny -= 1 - ry = height % ny # remainder + ry = height % ny # remainder else: ry = 0 @@ -835,10 +835,10 @@ def build_editable_mask_file(camera_id, mask_lines, capture_width=None, capture_ else: line_index_func = lambda y: (len(mask_lines) - 1) * y / ny - rh = height / ny # rectangle height + rh = height / ny # rectangle height # draw the actual mask image content - im = Image.new('L', (width, height), 255) # all white + im = Image.new('L', (width, height), 255) # all white dr = ImageDraw.Draw(im) for y in xrange(ny): @@ -864,7 +864,7 @@ def build_editable_mask_file(camera_id, mask_lines, capture_width=None, capture_ # resize the image if necessary if capture_width and capture_height and im.size != (capture_width, capture_height): logging.debug('editable mask needs resizing from %sx%s to %sx%s' % - (im.size[0], im.size[1], capture_width, capture_height)) + (im.size[0], im.size[1], capture_width, capture_height)) im = im.resize((capture_width, capture_height)) @@ -896,7 +896,7 @@ def parse_editable_mask_file(camera_id, capture_width=None, capture_height=None) # resize the image if necessary if im.size != (capture_width, capture_height): logging.debug('editable mask needs resizing from %sx%s to %sx%s' % - (im.size[0], im.size[1], capture_width, capture_height)) + (im.size[0], im.size[1], capture_width, capture_height)) im = im.resize((capture_width, capture_height)) @@ -910,26 +910,26 @@ def parse_editable_mask_file(camera_id, capture_width=None, capture_height=None) pixels = list(im.getdata()) # horizontal rectangles - nx = MASK_WIDTH # number of rectangles + nx = MASK_WIDTH # number of rectangles if width % nx: nx -= 1 - rx = width % nx # remainder + rx = width % nx # remainder else: rx = 0 - rw = width / nx # rectangle width + rw = width / nx # rectangle width # vertical rectangles - ny = height * MASK_WIDTH / width # number of rectangles + ny = height * MASK_WIDTH / width # number of rectangles if height % ny: ny -= 1 - ry = height % ny # remainder + ry = height % ny # remainder else: ry = 0 - rh = height / ny # rectangle height + rh = height / ny # rectangle height # parse the image contents and build the mask lines mask_lines = [width, height] diff --git a/motioneye/v4l2ctl.py b/motioneye/v4l2ctl.py index c8d84ac..cd8c2a3 100644 --- a/motioneye/v4l2ctl.py +++ b/motioneye/v4l2ctl.py @@ -38,7 +38,7 @@ def find_v4l2_ctl(): try: return subprocess.check_output(['which', 'v4l2-ctl'], stderr=utils.DEV_NULL).strip() - except subprocess.CalledProcessError: # not found + except subprocess.CalledProcessError: # not found return None @@ -85,7 +85,7 @@ def list_devices(): p.kill() except: - pass # nevermind + pass # nevermind name = None devices = [] @@ -124,8 +124,10 @@ def list_resolutions(device): resolutions = set() output = '' started = time.time() - p = subprocess.Popen('v4l2-ctl -d %(device)s --list-formats-ext | grep -vi stepwise | grep -oE "[0-9]+x[0-9]+" || true' % { - 'device': pipes.quote(device)}, shell=True, stdout=subprocess.PIPE, bufsize=1) + cmd = 'v4l2-ctl -d %(device)s --list-formats-ext | grep -vi stepwise | grep -oE "[0-9]+x[0-9]+" || true' % { + 'device': pipes.quote(device)} + + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=1) fd = p.stdout.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) @@ -156,7 +158,7 @@ def list_resolutions(device): p.kill() except: - pass # nevermind + pass # nevermind for pair in output.split('\n'): pair = pair.strip() @@ -168,9 +170,9 @@ def list_resolutions(device): height = int(height) if (width, height) in resolutions: - continue # duplicate resolution + continue # duplicate resolution - if width < 96 or height < 96: # some reasonable minimal values + if width < 96 or height < 96: # some reasonable minimal values continue if not motionctl.resolution_is_valid(width, height): @@ -326,8 +328,9 @@ def _set_ctrl(device, control, value): output = '' started = time.time() - p = subprocess.Popen('v4l2-ctl -d %(device)s --set-ctrl %(control)s=%(value)s' % { - 'device': pipes.quote(device), 'control': pipes.quote(control), 'value': pipes.quote(str(value))}, shell=True, stdout=subprocess.PIPE, bufsize=1) + cmd = 'v4l2-ctl -d %(device)s --set-ctrl %(control)s=%(value)s' % { + 'device': pipes.quote(device), 'control': pipes.quote(control), 'value': pipes.quote(str(value))} + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=1) fd = p.stdout.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) @@ -358,7 +361,7 @@ def _set_ctrl(device, control, value): p.kill() except: - pass # nevermind + pass # nevermind def _list_ctrls(device): @@ -403,14 +406,14 @@ def _list_ctrls(device): p.kill() except: - pass # nevermind + pass # nevermind controls = {} for line in output.split('\n'): if not line: continue - match = re.match('^\s*(\w+)\s+\(\w+\)\s*\:\s*(.+)\s*', line) + match = re.match('^\s*(\w+)\s+\(\w+\)\s*:\s*(.+)\s*', line) if not match: continue diff --git a/motioneye/webhook.py b/motioneye/webhook.py index bf90ba3..22ed799 100644 --- a/motioneye/webhook.py +++ b/motioneye/webhook.py @@ -52,19 +52,19 @@ def main(parser, args): headers['Content-Type'] = 'text/plain' data = '' - elif options.method == 'POSTf': # form url-encoded + elif options.method == 'POSTf': # form url-encoded headers['Content-Type'] = 'application/x-www-form-urlencoded' data = parts.query url = options.url.split('?')[0] - elif options.method == 'POSTj': # json + elif options.method == 'POSTj': # json headers['Content-Type'] = 'application/json' data = urlparse.parse_qs(parts.query) data = {k: v[0] for (k, v) in data.iteritems()} data = json.dumps(data) url = options.url.split('?')[0] - else: # GET + else: # GET pass request = urllib2.Request(url, data, headers=headers) diff --git a/motioneye/wifictl.py b/motioneye/wifictl.py index 3066ece..7f93ed8 100644 --- a/motioneye/wifictl.py +++ b/motioneye/wifictl.py @@ -160,7 +160,7 @@ def _set_wifi_settings(s): lines[i] = ' key_mgmt=' + key_mgmt + '\n' found_key_mgmt = True - else: # wifi disabled + else: # wifi disabled if re.match('ssid\s*=\s*".*?"', line) or re.match('psk\s*=\s*".*?"', line): lines.pop(i) i -= 1 diff --git a/motioneye/wsswitch.py b/motioneye/wsswitch.py index 3c34afb..2842fa2 100644 --- a/motioneye/wsswitch.py +++ b/motioneye/wsswitch.py @@ -34,21 +34,21 @@ def start(): def _during_working_schedule(now, working_schedule): parts = working_schedule.split('|') if len(parts) < 7: - return False # invalid ws + return False # invalid ws ws_day = parts[now.weekday()] parts = ws_day.split('-') if len(parts) != 2: - return False # invalid ws + return False # invalid ws _from, to = parts if not _from or not to: - return False # ws disabled for this day + return False # ws disabled for this day _from = _from.split(':') to = to.split(':') if len(_from) != 2 or len(to) != 2: - return False # invalid ws + return False # invalid ws try: from_h = int(_from[0]) @@ -57,7 +57,7 @@ def _during_working_schedule(now, working_schedule): to_m = int(to[1]) except ValueError: - return False # invalid ws + return False # invalid ws if now.hour < from_h or now.hour > to_h: return False @@ -80,7 +80,7 @@ def _check_ws(): return def on_motion_detection_status(camera_id, must_be_enabled, working_schedule_type, enabled=None, error=None): - if error: # could not detect current status + if error: # could not detect current status return logging.warn('skipping motion detection status update for camera with id %(id)s: %(error)s' % { 'id': camera_id, 'error': error}) @@ -108,14 +108,15 @@ def _check_ws(): motion_detection = camera_config.get('@motion_detection') working_schedule_type = camera_config.get('@working_schedule_type') or 'outside' - if not working_schedule: # working schedule disabled, motion detection left untouched + if not working_schedule: # working schedule disabled, motion detection left untouched continue - if not motion_detection: # motion detection explicitly disabled + if not motion_detection: # motion detection explicitly disabled continue now_during = _during_working_schedule(now, working_schedule) - must_be_enabled = (now_during and working_schedule_type == 'during') or (not now_during and working_schedule_type == 'outside') + must_be_enabled = ((now_during and working_schedule_type == 'during') or + (not now_during and working_schedule_type == 'outside')) motionctl.get_motion_detection(camera_id, functools.partial( on_motion_detection_status, camera_id, must_be_enabled, working_schedule_type))