def find_motion(): global _motion_binary_cache if _motion_binary_cache: return _motion_binary_cache if settings.MOTION_BINARY: if os.path.exists(settings.MOTION_BINARY): binary = settings.MOTION_BINARY else: return None, None else: # autodetect motion binary path try: binary = utils.call_subprocess(['which', 'motion']) except subprocess.CalledProcessError: # not found return None, None try: help = utils.call_subprocess(binary + ' -h || true', shell=True) except subprocess.CalledProcessError: # not found return None, None result = re.findall('motion Version ([^,]+)', help, re.IGNORECASE) version = result and result[0] or '' logging.debug('using motion version %s' % version) _motion_binary_cache = (binary, version) return _motion_binary_cache
def list_devices(): # currently MMAL support is designed specifically for the RPi; # therefore we can rely on the vcgencmd to report MMAL cameras logging.debug('listing MMAL devices') try: binary = utils.call_subprocess(['which', 'vcgencmd'], stderr=utils.DEV_NULL) except subprocess.CalledProcessError: # not found return [] try: support = utils.call_subprocess([binary, 'get_camera']) except subprocess.CalledProcessError: # not found return [] d = dict(p.split('=', 1) for p in support.split()) if d.get('detected') == d.get('supported') == '1': logging.debug('MMAL camera detected') return [('vc.ril.camera', 'VideoCore Camera')] return []
def _get_time_zone_md5(): if settings.LOCAL_TIME_FILE: return None try: output = utils.call_subprocess('find * -type f | xargs md5sum', shell=True, cwd='/usr/share/zoneinfo') except Exception as e: logging.error('getting md5 of zoneinfo files failed: %s' % e) return None lines = [l for l in output.split('\n') if l] lines = [l.split(None, 1) for l in lines] time_zone_by_md5 = dict(lines) try: with open(settings.LOCAL_TIME_FILE, 'rb') as f: data = f.read() except Exception as e: logging.error('failed to read local time file: %s' % e) return None md5 = hashlib.md5(data).hexdigest() time_zone = time_zone_by_md5.get(md5) if time_zone: logging.debug('found time zone by md5 method: %s' % time_zone) return time_zone
def find_v4l2_ctl(): try: return utils.call_subprocess(['which', 'v4l2-ctl'], stderr=utils.DEV_NULL) except subprocess.CalledProcessError: # not found return None
def get(self, name): log = self.LOGS.get(name) if log is None: raise HTTPError(404, 'no such log') (path, filename) = log self.set_header('Content-Type', 'text/plain') self.set_header('Content-Disposition', 'attachment; filename=' + filename + ';') 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 logging.debug('serving log file "%s" from command "%s"' % (filename, path)) try: output = utils.call_subprocess(path.split()) except Exception as e: output = 'failed to execute command: %s' % e self.finish(output)
def _get_os_version_uname(): try: output = utils.call_subprocess('uname -rs', shell=True) lines = output.strip().split() name, version = lines return name, version except: return 'Linux', '' # most likely :)
def _get_os_version_lsb_release(): try: output = utils.call_subprocess('lsb_release -sri', shell=True) lines = output.strip().split() name, version = lines if version.lower() == 'rolling': version = '' return name, version except: return _get_os_version_uname()
def list_ctrls(device): global _ctrls_cache device = utils.make_str(device) if device in _ctrls_cache: return _ctrls_cache[device] output = b'' started = time.time() cmd = 'v4l2-ctl -d %(device)s --list-ctrls' actual_cmd = cmd % {'device': pipes.quote(device)} logging.debug('running command "%s"' % actual_cmd) try: output = utils.call_subprocess(actual_cmd, shell=True, stderr=subprocess.STDOUT) except: logging.error('failed to list controls of device %(device)s...' % {'device': device}) controls = {} logging.debug(' command output "%s"' % output) output = utils.make_str(output) for line in output.split('\n'): if not line: continue match = re.match(r'^\s*(\w+)\s+([a-f0-9x\s]+)?\(\w+\)\s*:\s*(.+)\s*', line) if not match: continue (control, _, properties) = match.groups() properties = dict( [v.split('=', 1) for v in properties.split(' ') if v.count('=')]) controls[control] = properties _ctrls_cache[device] = controls return controls
def list_devices(): global _resolutions_cache, _ctrls_cache, _ctrl_values_cache logging.debug('listing V4L2 devices') output = b'' try: output = utils.call_subprocess(['v4l2-ctl', '--list-devices'], stderr=subprocess.STDOUT) except: logging.debug('v4l2-ctl error : %s ' % output) name = None devices = [] output = utils.make_str(output) for line in output.split('\n'): if line.startswith('\t'): device = line.strip() persistent_device = find_persistent_device(device) devices.append((device, persistent_device, name)) logging.debug( 'found device %(name)s: %(device)s, %(persistent_device)s' % { 'name': name, 'device': device, 'persistent_device': persistent_device }) else: name = line.split('(')[0].strip() # clear the cache _resolutions_cache = {} _ctrls_cache = {} _ctrl_values_cache = {} return devices
def list_resolutions(device): from motioneye import motionctl global _resolutions_cache device = utils.make_str(device) if device in _resolutions_cache: return _resolutions_cache[device] logging.debug('listing resolutions of device %(device)s...' % {'device': device}) resolutions = set() output = b'' started = time.time() cmd = 'v4l2-ctl -d %(device)s --list-formats-ext | grep -vi stepwise | grep -oE "[0-9]+x[0-9]+" || true' actual_cmd = cmd % {'device': pipes.quote(device)} logging.debug('running command "%s"' % actual_cmd) try: output = utils.call_subprocess(actual_cmd, shell=True, stderr=utils.DEV_NULL) except: logging.error('failed to list resolutions of device %(device)s...' % {'device': device}) output = utils.make_str(output) for pair in output.split('\n'): pair = pair.strip() if not pair: continue width, height = pair.split('x') width = int(width) height = int(height) if (width, height) in resolutions: continue # duplicate resolution if width < 96 or height < 96: # some reasonable minimal values continue if not motionctl.resolution_is_valid(width, height): continue resolutions.add((width, height)) logging.debug( 'found resolution %(width)sx%(height)s for device %(device)s' % { 'device': device, 'width': width, 'height': height }) if not resolutions: logging.debug( 'no resolutions found for device %(device)s, using common values' % {'device': device}) # no resolution returned by v4l2-ctl call, add common default resolutions resolutions = utils.COMMON_RESOLUTIONS resolutions = [ r for r in resolutions if motionctl.resolution_is_valid(*r) ] resolutions = list(sorted(resolutions, key=lambda r: (r[0], r[1]))) _resolutions_cache[device] = resolutions return resolutions
def _list_disks_fdisk(): try: output = utils.call_subprocess(['fdisk', '-l'], stderr=utils.DEV_NULL) except Exception as e: logging.error('failed to list disks using "fdisk -l": %s' % e, exc_info=True) return [] disks = [] disk = None def add_disk(d): logging.debug('found disk at "%s" on bus "%s": "%s %s"' % (d['target'], d['bus'], d['vendor'], d['model'])) 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(d) for line in output.split('\n'): line = line.replace('*', '') line = re.sub('\s+', ' ', line.strip()) if not line: continue if line.startswith('Disk /dev/'): if disk and disk['partitions']: add_disk(disk) parts = line.split() disk = { 'target': parts[1].strip(':'), 'bus': '', 'vendor': '', 'model': parts[2] + ' ' + parts[3].strip(','), 'partitions': [] } elif line.startswith('/dev/') and disk: parts = line.split() part_no = re.findall(r'\d+$', parts[0]) partition = { 'part_no': int(part_no[0]) if part_no else None, 'target': parts[0], 'bus': '', 'vendor': '', 'model': parts[4] + ' ' + ' '.join(parts[6:]), } disk['partitions'].append(partition) if disk and disk['partitions']: add_disk(disk) disks.sort(key=lambda d: d['target']) for disk in disks: disk['partitions'].sort(key=lambda p: p['part_no']) return disks
def make_movie_preview(camera_config: dict, full_path: str) -> typing.Union[str, None]: framerate = camera_config['framerate'] pre_capture = camera_config['pre_capture'] offs = pre_capture / framerate offs = max(4, offs * 2) thumb_path = full_path + '.thumb' logging.debug('creating movie preview for %(path)s with an offset of %(offs)s seconds...' % { 'path': full_path, 'offs': offs}) cmd = 'ffmpeg -i %(path)s -f mjpeg -vframes 1 -ss %(offs)s -y %(path)s.thumb' actual_cmd = cmd % {'path': pipes.quote(full_path), 'offs': offs} logging.debug('running command "%s"' % actual_cmd) try: utils.call_subprocess(actual_cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logging.error('failed to create movie preview for %(path)s: %(msg)s' % { 'path': full_path, 'msg': str(e)}) return None try: st = os.stat(thumb_path) except os.error: logging.error('failed to create movie preview for %(path)s' % {'path': full_path}) return None if st.st_size == 0: logging.debug('movie probably too short, grabbing first frame from %(path)s...' % {'path': full_path}) actual_cmd = cmd % {'path': full_path, 'offs': 0} logging.debug('running command "%s"' % actual_cmd) # try again, this time grabbing the very first frame try: utils.call_subprocess(actual_cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logging.error('failed to create movie preview for %(path)s: %(msg)s' % { 'path': full_path, 'msg': str(e)}) return None try: st = os.stat(thumb_path) except os.error: logging.error('failed to create movie preview for %(path)s' % {'path': full_path}) return None if st.st_size == 0: logging.error('failed to create movie preview for %(path)s' % {'path': full_path}) try: os.remove(thumb_path) except: pass return None return thumb_path
def find_ffmpeg() -> tuple: global _ffmpeg_binary_cache if _ffmpeg_binary_cache: return _ffmpeg_binary_cache # binary try: binary = utils.call_subprocess(['which', 'ffmpeg'], stderr=utils.DEV_NULL) except subprocess.CalledProcessError: # not found return None, None, None # version try: output = utils.call_subprocess(binary + ' -version', shell=True) except subprocess.CalledProcessError as e: logging.error('ffmpeg: could find version: %s' % e) return None, None, None result = re.findall('ffmpeg version (.+?) ', output, re.IGNORECASE) version = result and result[0] or '' # codecs try: output = utils.call_subprocess(binary + ' -codecs -hide_banner', shell=True) except subprocess.CalledProcessError as e: logging.error('ffmpeg: could not list supported codecs: %s' % e) return None, None, None lines = output.split('\n') lines = [l for l in lines if re.match('^ [DEVILSA.]{6} [^=].*', l)] codecs = {} for line in lines: m = re.match('^ [DEVILSA.]{6} ([\w+_]+)', line) if not m: continue codec = m.group(1) decoders = set() encoders = set() m = re.search('decoders: ([\w\s_]+)+', line) if m: decoders = set(m.group(1).split()) m = re.search('encoders: ([\w\s_]+)+', line) if m: encoders = set(m.group(1).split()) codecs[codec] = { 'encoders': encoders, 'decoders': decoders } logging.debug('using ffmpeg version %s' % version) _ffmpeg_binary_cache = (binary, version, codecs) return _ffmpeg_binary_cache
def _find_prog(prog: str) -> str: return utils.call_subprocess(['which', prog])
def find_mount_cifs(): try: return utils.call_subprocess(['which', 'mount.cifs']) except subprocess.CalledProcessError: # not found return None