예제 #1
0
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
예제 #2
0
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 []
예제 #3
0
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
예제 #4
0
def find_v4l2_ctl():
    try:
        return utils.call_subprocess(['which', 'v4l2-ctl'],
                                     stderr=utils.DEV_NULL)

    except subprocess.CalledProcessError:  # not found
        return None
예제 #5
0
    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)
예제 #6
0
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 :)
예제 #7
0
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()
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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
예제 #11
0
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
예제 #12
0
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
예제 #13
0
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
예제 #14
0
 def _find_prog(prog: str) -> str:
     return utils.call_subprocess(['which', prog])
예제 #15
0
def find_mount_cifs():
    try:
        return utils.call_subprocess(['which', 'mount.cifs'])

    except subprocess.CalledProcessError:  # not found
        return None