Exemplo n.º 1
0
async def _check_ws() -> None:
    # schedule the next call
    io_loop = IOLoop.instance()
    io_loop.add_timeout(datetime.timedelta(seconds=10), _start_check_ws)

    if not motionctl.running():
        return

    now = datetime.datetime.now()
    for camera_id in config.get_camera_ids():
        camera_config = config.get_camera(camera_id)
        if not utils.is_local_motion_camera(camera_config):
            continue

        working_schedule = camera_config.get('@working_schedule')
        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
            continue

        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'))

        motion_detection_resp = await motionctl.get_motion_detection(camera_id)
        await _switch_motion_detection_status(camera_id, must_be_enabled,
                                              working_schedule_type,
                                              motion_detection_resp)
Exemplo n.º 2
0
    async def preview(self, camera_id, filename):
        logging.debug('previewing picture %(filename)s of camera %(id)s' % {
            'filename': filename, 'id': camera_id})

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            content = mediafiles.get_media_preview(camera_config, filename, 'picture',
                                                   width=self.get_argument('width', None),
                                                   height=self.get_argument('height', None))

            if content:
                self.set_header('Content-Type', 'image/jpeg')

            else:
                self.set_header('Content-Type', 'image/svg+xml')
                content = open(os.path.join(settings.STATIC_PATH, 'img', 'no-preview.svg'), 'rb').read()

            return self.finish(content)

        elif utils.is_remote_camera(camera_config):
            resp = await remote.get_media_preview(camera_config, filename=filename, media_type='picture',
                                                  width=self.get_argument('width', None),
                                                  height=self.get_argument('height', None))
            content = resp.result
            if content:
                self.set_header('Content-Type', 'image/jpeg')

            else:
                self.set_header('Content-Type', 'image/svg+xml')
                content = open(os.path.join(settings.STATIC_PATH, 'img', 'no-preview.svg')).read()

            return self.finish(content)

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 3
0
def get_jpg(camera_id):
    if camera_id not in MjpgClient.clients:
        # mjpg client not started yet for this camera

        logging.debug('creating mjpg client for camera %(camera_id)s' %
                      {'camera_id': camera_id})

        camera_config = config.get_camera(camera_id)
        if not camera_config['@enabled'] or not utils.is_local_motion_camera(
                camera_config):
            logging.error(
                'could not start mjpg client for camera id %(camera_id)s: not enabled or not local'
                % {'camera_id': camera_id})

            return None

        port = camera_config['stream_port']
        username, password = None, None
        auth_mode = None
        if camera_config.get('stream_auth_method') > 0:
            username, password = camera_config.get('stream_authentication',
                                                   ':').split(':')
            auth_mode = 'digest' if camera_config.get(
                'stream_auth_method') > 1 else 'basic'

        client = MjpgClient(camera_id, port, username, password, auth_mode)
        client.do_connect()

        MjpgClient.clients[camera_id] = client

    client = MjpgClient.clients[camera_id]

    return client.get_last_jpg()
Exemplo n.º 4
0
    async def delete(self, camera_id, filename):
        logging.debug('deleting movie %(filename)s of camera %(id)s' % {
            'filename': filename,
            'id': camera_id
        })

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            try:
                mediafiles.del_media_content(camera_config, filename, 'movie')
                return self.finish_json()

            except Exception as e:
                return self.finish_json({'error': str(e)})

        elif utils.is_remote_camera(camera_config):
            resp = await remote.del_media_content(camera_config,
                                                  filename=filename,
                                                  media_type='movie')
            if resp.error:
                return self.finish_json({
                    'error':
                    'Failed to delete movie from %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config),
                        'msg': resp.error
                    }
                })

            return self.finish_json()

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 5
0
    async 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):

            return self.render('main.html', frame=True, camera_id=camera_id, camera_config=camera_config,
                               title=self.get_argument('title', camera_config.get('camera_name', '')),
                               admin_username=config.get_main().get('@admin_username'),
                               static_path='../../../static/')

        elif utils.is_remote_camera(camera_config):
            resp = await remote.get_config(camera_config)
            if resp.error:
                return self.render('main.html',
                                   frame=True,
                                   camera_id=camera_id,
                                   camera_config=camera_config,
                                   title=self.get_argument('title', ''))

            # issue a fake motion_camera_ui_to_dict() call to transform
            # the remote UI values into motion config directives
            remote_config = config.motion_camera_ui_to_dict(resp.remote_ui_config)

            return self.render('main.html', frame=True, camera_id=camera_id, camera_config=remote_config,
                               title=self.get_argument('title', remote_config['camera_name']),
                               admin_username=config.get_main().get('@admin_username'))
Exemplo n.º 6
0
    async def download(self, camera_id, filename):
        logging.debug('downloading picture %(filename)s of camera %(id)s' % {
            'filename': filename, 'id': camera_id})

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            content = mediafiles.get_media_content(camera_config, filename, 'picture')

            pretty_filename = camera_config['camera_name'] + '_' + os.path.basename(filename)
            self.set_header('Content-Type', 'image/jpeg')
            self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';')

            return self.finish(content)

        elif utils.is_remote_camera(camera_config):
            resp = await remote.get_media_content(camera_config, filename=filename, media_type='picture')
            if resp.error:
                return self.finish_json({'error': 'Failed to download picture from %(url)s: %(msg)s.' % {
                    'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}})

            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 + ';')

            return self.finish(resp.result)

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 7
0
    async def list(self, camera_id):
        logging.debug('listing movies for camera %(id)s' % {'id': camera_id})

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            media_list = await mediafiles.list_media(camera_config,
                                                     media_type='movie',
                                                     prefix=self.get_argument(
                                                         'prefix', None))
            if media_list is None:
                self.finish_json({'error': 'Failed to get movies list.'})

            return self.finish_json({
                'mediaList': media_list,
                'cameraName': camera_config['camera_name']
            })

        elif utils.is_remote_camera(camera_config):
            resp = await remote.list_media(camera_config,
                                           media_type='movie',
                                           prefix=self.get_argument(
                                               'prefix', None))
            if resp.error:
                return self.finish_json({
                    'error':
                    'Failed to get movie list for %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config),
                        'msg': resp.error
                    }
                })

            return self.finish_json(resp.media_list)

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 8
0
    async def delete_all(self, camera_id, group):
        logging.debug('deleting movie group "%(group)s" of camera %(id)s' % {
            'group': group or 'ungrouped',
            'id': camera_id
        })

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            try:
                mediafiles.del_media_group(camera_config, group, 'movie')
                return self.finish_json()

            except Exception as e:
                return self.finish_json({'error': str(e)})

        elif utils.is_remote_camera(camera_config):
            resp = await remote.del_media_group(camera_config,
                                                group=group,
                                                media_type='movie')
            if resp.error:
                return self.finish_json({
                    'error':
                    'Failed to delete movie group at %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config),
                        'msg': resp.error
                    }
                })

            return self.finish_json()

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 9
0
    async def zipped(self, camera_id, group):
        key = self.get_argument('key', None)
        camera_config = config.get_camera(camera_id)

        if key:
            logging.debug('serving zip file for group "%(group)s" of camera %(id)s with key %(key)s' % {
                'group': group or 'ungrouped', 'id': camera_id, 'key': key})

            if utils.is_local_motion_camera(camera_config):
                data = mediafiles.get_prepared_cache(key)
                if not data:
                    logging.error('prepared cache data for key "%s" does not exist' % key)

                    raise HTTPError(404, 'no such key')

                pretty_filename = camera_config['camera_name'] + '_' + group
                pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename)

                self.set_header('Content-Type', 'application/zip')
                self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + '.zip;')
                return self.finish(data)

            elif utils.is_remote_camera(camera_config):
                resp = await remote.get_zipped_content(camera_config, media_type='picture', key=key, group=group)
                if resp.error:
                    return self.finish_json({'error': 'Failed to download zip file from %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}})

                self.set_header('Content-Type', resp.result['content_type'])
                self.set_header('Content-Disposition', resp.result['content_disposition'])
                return self.finish(resp.result['data'])

            else:  # assuming simple mjpeg camera
                raise HTTPError(400, 'unknown operation')

        else:  # prepare
            logging.debug('preparing zip file for group "%(group)s" of camera %(id)s' % {
                'group': group or 'ungrouped', 'id': camera_id})

            if utils.is_local_motion_camera(camera_config):
                data = await mediafiles.get_zipped_content(camera_config, media_type='picture', group=group)
                if data is None:
                    return self.finish_json({'error': 'Failed to create zip file.'})

                key = mediafiles.set_prepared_cache(data)
                logging.debug('prepared zip file for group "%(group)s" of camera %(id)s with key %(key)s' % {
                    'group': group or 'ungrouped', 'id': camera_id, 'key': key})
                self.finish_json({'key': key})

            elif utils.is_remote_camera(camera_config):
                resp = await remote.make_zipped_content(camera_config, media_type='picture', group=group)
                if resp.error:
                    return self.finish_json({'error': 'Failed to make zip file at %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}})

                return self.finish_json({'key': resp.result['key']})

            else:  # assuming simple mjpeg camera
                raise HTTPError(400, 'unknown operation')
Exemplo n.º 10
0
    def rem_camera(self, camera_id):
        logging.debug('removing camera %(id)s' % {'id': camera_id})

        local = utils.is_local_motion_camera(config.get_camera(camera_id))
        config.rem_camera(camera_id)

        if local:
            motionctl.stop()
            motionctl.start()

        return self.finish_json()
Exemplo n.º 11
0
async def _disable_initial_motion_detection():
    from motioneye import config

    for camera_id in config.get_camera_ids():
        camera_config = config.get_camera(camera_id)
        if not utils.is_local_motion_camera(camera_config):
            continue

        if not camera_config['@motion_detection']:
            logging.debug(
                'motion detection disabled by config for camera with id %s' %
                camera_id)
            await set_motion_detection(camera_id, False)
Exemplo n.º 12
0
def make_media_folders():
    from motioneye import config

    config.get_main()  # just to have main config already loaded

    camera_ids = config.get_camera_ids()
    for camera_id in camera_ids:
        camera_config = config.get_camera(camera_id)
        if 'target_dir' in camera_config:
            if not os.path.exists(camera_config['target_dir']):
                try:
                    os.makedirs(camera_config['target_dir'])

                except Exception as e:
                    logging.error(
                        'failed to create root media folder "%s" for camera with id %s: %s'
                        % (camera_config['target_dir'], camera_id, e))
Exemplo n.º 13
0
        async def set_camera_config(camera_id, ui_config, on_finish):
            logging.debug('setting config for camera %(id)s...' %
                          {'id': camera_id})

            if camera_id not in camera_ids:
                raise HTTPError(404, 'no such camera')

            local_config = config.get_camera(camera_id)
            if utils.is_local_motion_camera(local_config):
                local_config = config.motion_camera_ui_to_dict(
                    ui_config, local_config)

                config.set_camera(camera_id, local_config)

                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 'name' in ui_config:

                    def on_finish_wrapper(e=None):
                        return on_finish(e, False)

                    ui_config[
                        'enabled'] = True  # never disable the camera remotely
                    result = await remote.set_config(local_config, ui_config)
                    return on_finish(result, False)

                else:
                    # when the ui config supplied has only the enabled state
                    # and no useful fields (such as "name"),
                    # the camera was probably disabled due to errors
                    on_finish(None, False)

            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)
Exemplo n.º 14
0
    async def post(self, camera_id, action):
        camera_id = int(camera_id)
        if camera_id not in config.get_camera_ids():
            raise HTTPError(404, 'no such camera')

        local_config = config.get_camera(camera_id)
        if utils.is_remote_camera(local_config):
            resp = await remote.exec_action(local_config, action)
            if resp.error:
                msg = 'Failed to execute action on remote camera at %(url)s: %(msg)s.' % {
                    'url': remote.pretty_camera_url(local_config),
                    'msg': resp.error
                }

                return self.finish_json({'error': msg})

            return self.finish_json()

        if action == 'snapshot':
            logging.debug('executing snapshot action for camera with id %s' %
                          camera_id)
            await self.snapshot(camera_id)
            return

        elif action == 'record_start':
            logging.debug(
                'executing record_start action for camera with id %s' %
                camera_id)
            return self.record_start(camera_id)

        elif action == 'record_stop':
            logging.debug(
                'executing record_stop action for camera with id %s' %
                camera_id)
            return self.record_stop(camera_id)

        action_commands = config.get_action_commands(local_config)
        command = action_commands.get(action)
        if not command:
            raise HTTPError(400, 'unknown action')

        logging.debug('executing %s action for camera with id %s: "%s"' %
                      (action, camera_id, command))
        self.run_command_bg(command)
Exemplo n.º 15
0
    async def current(self, camera_id, retry=0):
        self.set_header('Content-Type', 'image/jpeg')
        self.set_header('Cache-Control', 'no-store, must-revalidate')
        self.set_header('Pragma', 'no-cache')
        self.set_header('Expires', '0')

        width = self.get_argument('width', None)
        height = self.get_argument('height', None)

        width = width and float(width)
        height = height and float(height)

        camera_id_str = str(camera_id)

        camera_config = config.get_camera(camera_id)
        if utils.is_local_motion_camera(camera_config):
            picture = mediafiles.get_current_picture(camera_config, width=width, height=height)

            # picture is not available usually when the corresponding internal mjpeg client has been closed;
            # get_current_picture() will make sure to start a client, but a jpeg frame is not available right away;
            # wait at most 5 seconds and retry every 200 ms.
            if not picture and retry < 25:
                return IOLoop.instance().add_timeout(datetime.timedelta(seconds=0.2), self.current,
                                                     camera_id=camera_id, retry=retry + 1)

            self.set_cookie('motion_detected_' + camera_id_str, str(motionctl.is_motion_detected(camera_id)).lower())
            self.set_cookie('capture_fps_' + camera_id_str, '%.1f' % mjpgclient.get_fps(camera_id))
            self.set_cookie('monitor_info_' + camera_id_str, monitor.get_monitor_info(camera_id))

            return self.try_finish(picture)

        elif utils.is_remote_camera(camera_config):
            resp = await remote.get_current_picture(camera_config, width=width, height=height)
            if resp.error:
                return self.try_finish(None)

            self.set_cookie('motion_detected_' + camera_id_str, str(resp.motion_detected).lower())
            self.set_cookie('capture_fps_' + camera_id_str, '%.1f' % resp.capture_fps)
            self.set_cookie('monitor_info_' + camera_id_str, resp.monitor_info or '')

            return self.try_finish(resp.picture)

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 16
0
def cleanup_media(media_type: str) -> None:
    logging.debug('cleaning up %(media_type)ss...' % {'media_type': media_type})

    if media_type == 'picture':
        exts = _PICTURE_EXTS

    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):
            continue

        preserve_media = camera_config.get('@preserve_%(media_type)ss' % {'media_type': media_type}, 0)
        if preserve_media == 0:
            continue  # preserve forever

        still_images_enabled = bool(camera_config['picture_filename']) or bool(camera_config['snapshot_filename'])
        movies_enabled = bool(camera_config['movie_output'])

        if media_type == 'picture' and not 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

        preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_media)

        target_dir = camera_config.get('target_dir')
        cloud_enabled = camera_config.get('@upload_enabled')
        clean_cloud_enabled = camera_config.get('@clean_cloud_enabled')
        cloud_dir = camera_config.get('@upload_location')
        service_name = camera_config.get('@upload_service')
        clean_cloud_info = None
        if cloud_enabled and clean_cloud_enabled and camera_id and service_name and cloud_dir:
            clean_cloud_info = {'camera_id': camera_id, 'service_name': service_name, 'cloud_dir': cloud_dir}
        if os.path.exists(target_dir):
            # create a sentinel file to make sure the target dir is never removed
            open(os.path.join(target_dir, '.keep'), 'w').close()

        logging.debug('calling _remove_older_files: %s %s %s' % (cloud_enabled, clean_cloud_enabled, clean_cloud_info))
        _remove_older_files(target_dir, preserve_moment, clean_cloud_info, exts=exts)
Exemplo n.º 17
0
    async def get_config(self, camera_id):
        if camera_id:
            logging.debug('getting config for camera %(id)s' %
                          {'id': camera_id})

            if camera_id not in config.get_camera_ids():
                raise HTTPError(404, 'no such camera')

            local_config = config.get_camera(camera_id)
            if utils.is_local_motion_camera(local_config):
                ui_config = config.motion_camera_dict_to_ui(local_config)

                return self.finish_json(ui_config)

            elif utils.is_remote_camera(local_config):
                resp = await remote.get_config(local_config)
                if resp.error:
                    msg = 'Failed to get remote camera configuration for %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(local_config),
                        'msg': resp.error
                    }
                    return self.finish_json_with_error(msg)

                for key, value in list(local_config.items()):
                    resp.remote_ui_config[key.replace('@', '')] = value

                # replace the real device url with the remote camera path
                resp.remote_ui_config['device_url'] = remote.pretty_camera_url(
                    local_config)
                return self.finish_json(resp.remote_ui_config)

            else:  # assuming simple mjpeg camera
                ui_config = config.simple_mjpeg_camera_dict_to_ui(local_config)

                return self.finish_json(ui_config)

        else:
            logging.debug('getting main config')

            ui_config = config.main_dict_to_ui(config.get_main())
            return self.finish_json(ui_config)
Exemplo n.º 18
0
    def post(self):
        event = self.get_argument('event')
        motion_camera_id = int(self.get_argument('motion_camera_id'))

        camera_id = motionctl.motion_camera_id_to_camera_id(motion_camera_id)
        if camera_id is None:
            logging.debug('ignoring event for unknown motion camera id %s' %
                          motion_camera_id)
            return self.finish_json()

        else:
            logging.debug(
                'received relayed event %(event)s for motion camera id %(id)s (camera id %(cid)s)'
                % {
                    'event': event,
                    'id': motion_camera_id,
                    'cid': camera_id
                })

        camera_config = config.get_camera(camera_id)
        if not utils.is_local_motion_camera(camera_config):
            logging.warning('ignoring event for non-local camera with id %s' %
                            camera_id)
            return self.finish_json()

        if event == 'start':
            if not camera_config['@motion_detection']:
                logging.debug(
                    'ignoring start event for camera with id %s and motion detection disabled'
                    % camera_id)
                return self.finish_json()

            motionctl.set_motion_detected(camera_id, True)

        elif event == 'stop':
            motionctl.set_motion_detected(camera_id, False)

        elif event == 'movie_end':
            filename = self.get_argument('filename')

            # generate preview (thumbnail)
            tasks.add(5,
                      mediafiles.make_movie_preview,
                      tag='make_movie_preview(%s)' % filename,
                      camera_config=camera_config,
                      full_path=filename)

            # upload to external service
            if camera_config['@upload_enabled'] and camera_config[
                    '@upload_movie']:
                self.upload_media_file(filename, camera_id, camera_config)

        elif event == 'picture_save':
            filename = self.get_argument('filename')

            # upload to external service
            if camera_config['@upload_enabled'] and camera_config[
                    '@upload_picture']:
                self.upload_media_file(filename, camera_id, camera_config)

        else:
            logging.warning('unknown event %s' % event)

        self.finish_json()
Exemplo n.º 19
0
    async def get(self, camera_id, filename=None, include_body=True):
        logging.debug('downloading movie %(filename)s of camera %(id)s' % {
            'filename': filename,
            'id': camera_id
        })

        self.pretty_filename = os.path.basename(filename)

        if camera_id is not None:
            camera_id = int(camera_id)
            if camera_id not in config.get_camera_ids():
                raise HTTPError(404, 'no such camera')

        camera_config = config.get_camera(camera_id)

        if utils.is_local_motion_camera(camera_config):
            filename = mediafiles.get_media_path(camera_config, filename,
                                                 'movie')
            self.pretty_filename = camera_config[
                'camera_name'] + '_' + self.pretty_filename
            await StaticFileHandler.get(self,
                                        filename,
                                        include_body=include_body)
            return

        elif utils.is_remote_camera(camera_config):
            # we will cache the movie since it takes a while to fetch from the remote camera
            # and we may be going to play it back in the browser, which will fetch the video in chunks
            tmpfile = self.tmpdir + '/' + self.pretty_filename
            if os.path.isfile(tmpfile):
                # have a cached copy, update the timestamp so it's not flushed
                import time
                mtime = os.stat(tmpfile).st_mtime
                os.utime(tmpfile, (time.time(), mtime))
                await StaticFileHandler.get(self,
                                            tmpfile,
                                            include_body=include_body)
                return

            resp = await remote.get_media_content(camera_config,
                                                  filename,
                                                  media_type='movie')
            if resp.error:
                return self.finish_json({
                    'error':
                    'Failed to download movie from %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config),
                        'msg': resp.error
                    }
                })

            # check if the file has been created by another request while we were fetching the movie
            if not os.path.isfile(tmpfile):
                tmp = open(tmpfile, 'wb')
                tmp.write(resp.result)
                tmp.close()

            await StaticFileHandler.get(self,
                                        tmpfile,
                                        include_body=include_body)
            return

        else:  # assuming simple mjpeg camera
            raise HTTPError(400, 'unknown operation')
Exemplo n.º 20
0
        def set_main_config(ui_config):
            logging.debug('setting main config...')

            old_main_config = config.get_main()
            old_admin_username = old_main_config.get('@admin_username')
            old_normal_username = old_main_config.get('@normal_username')

            main_config = config.main_ui_to_dict(ui_config)
            main_config.setdefault('camera', old_main_config.get('camera', []))

            admin_username = main_config.get('@admin_username')
            admin_password = main_config.get('@admin_password')

            normal_username = main_config.get('@normal_username')
            normal_password = main_config.get('@normal_password')

            additional_configs = config.get_additional_structure(
                camera=False)[1]
            reboot_config_names = [('@_' + c['name'])
                                   for c in list(additional_configs.values())
                                   if c.get('reboot')]
            reboot = bool([
                k for k in reboot_config_names
                if old_main_config.get(k) != main_config.get(k)
            ])

            config.set_main(main_config)

            reload = False
            restart = False

            if admin_username != old_admin_username or admin_password is not None:
                logging.debug('admin credentials changed, reload needed')

                reload = True

            if normal_username != old_normal_username or normal_password is not None:
                logging.debug(
                    'surveillance credentials changed, all camera configs must be updated'
                )

                # reconfigure all local cameras to update the stream authentication options
                for camera_id in config.get_camera_ids():
                    local_config = config.get_camera(camera_id)
                    if not utils.is_local_motion_camera(local_config):
                        continue

                    ui_config = config.motion_camera_dict_to_ui(local_config)
                    local_config = config.motion_camera_ui_to_dict(
                        ui_config, local_config)

                    config.set_camera(camera_id, local_config)

                    restart = True

            if reboot and settings.ENABLE_REBOOT:
                logging.debug('system settings changed, reboot needed')

            else:
                reboot = False

            return {'reload': reload, 'reboot': reboot, 'restart': restart}
Exemplo n.º 21
0
    async def list(self):
        logging.debug('listing cameras')

        proto = self.get_argument('proto')
        if proto == 'motioneye':  # remote listing
            return self._handle_list_cameras_response(
                await remote.list_cameras(self.get_all_arguments()))

        elif proto == 'netcam':
            scheme = self.get_argument('scheme', 'http')

            if scheme in ['http', 'https', 'mjpeg']:
                resp = await test_mjpeg_url(self.get_all_arguments(),
                                            auth_modes=['basic'],
                                            allow_jpeg=True)
                return self._handle_list_cameras_response(resp)

            elif scheme == 'rtsp':
                resp = await test_rtsp_url(self.get_all_arguments())
                return self._handle_list_cameras_response(resp)

            elif scheme == 'rtmp':
                resp = test_rtmp_url(self.get_all_arguments())
                return self._handle_list_cameras_response(resp)

            else:
                return self.finish_json_with_error(
                    f'protocol {scheme} not supported')

        elif proto == 'mjpeg':
            resp = await test_mjpeg_url(self.get_all_arguments(),
                                        auth_modes=['basic', 'digest'],
                                        allow_jpeg=False)
            return self._handle_list_cameras_response(resp)

        elif proto == 'v4l2':
            configured_devices = set()
            for camera_id in config.get_camera_ids():
                data = config.get_camera(camera_id)
                if utils.is_v4l2_camera(data):
                    configured_devices.add(data['videodevice'])

            cameras = [{
                'id': d[1],
                'name': d[2]
            } for d in v4l2ctl.list_devices()
                       if (d[0] not in configured_devices) and (
                           d[1] not in configured_devices)]

            return self.finish_json({'cameras': cameras})

        elif proto == 'mmal':
            configured_devices = set()
            for camera_id in config.get_camera_ids():
                data = config.get_camera(camera_id)
                if utils.is_mmal_camera(data):
                    configured_devices.add(data['mmalcam_name'])

            cameras = [{
                'id': d[0],
                'name': d[1]
            } for d in mmalctl.list_devices()
                       if (d[0] not in configured_devices)]

            return self.finish_json({'cameras': cameras})

        else:  # assuming local motionEye camera listing
            cameras = []
            camera_ids = config.get_camera_ids()
            if not config.get_main().get('@enabled'):
                camera_ids = []

            length = [len(camera_ids)]

            for camera_id in camera_ids:
                local_config = config.get_camera(camera_id)
                if local_config is None:
                    continue

                if utils.is_local_motion_camera(local_config):
                    ui_config = config.motion_camera_dict_to_ui(local_config)
                    cameras.append(ui_config)
                    finished = self.check_finished(cameras, length)
                    if finished:
                        return

                elif utils.is_remote_camera(local_config):
                    if local_config.get('@enabled') or self.get_argument(
                            'force', None) == 'true':
                        resp = await remote.get_config(local_config)
                        return self._handle_get_config_response(
                            camera_id, local_config, resp, cameras, length)

                    else:  # don't try to reach the remote of the camera is disabled
                        return self._handle_get_config_response(
                            camera_id, local_config,
                            utils.GetConfigResponse(None, error=True), cameras,
                            length)

                else:  # assuming simple mjpeg camera
                    ui_config = config.simple_mjpeg_camera_dict_to_ui(
                        local_config)
                    cameras.append(ui_config)
                    return self.check_finished(cameras, length)

            if length[0] == 0:
                return self.finish_json({'cameras': []})
Exemplo n.º 22
0
    async def test(self, camera_id):
        what = self.get_argument('what')
        data = self.get_all_arguments()
        camera_config = config.get_camera(camera_id)

        if utils.is_local_motion_camera(camera_config):
            if what == 'upload_service':
                service_name = data['service']
                ConfigHandler._upload_service_test_info = (self, service_name)

                result = uploadservices.test_access(camera_id=camera_id,
                                                    service_name=service_name,
                                                    data=data)
                logging.debug('test access %s result %s' %
                              (service_name, result))
                if result is True:
                    logging.info('accessing %s succeeded.result %s' %
                                 (service_name, result))
                    return self.finish_json()
                else:
                    logging.warning('accessing %s failed: %s' %
                                    (service_name, result))
                    return self.finish_json({'error': result})

            elif what == 'email':
                from motioneye import sendmail
                import smtplib

                logging.debug('testing notification email')

                try:
                    subject = sendmail.subjects['motion_start']
                    message = sendmail.messages['motion_start']
                    format_dict = {
                        'camera':
                        camera_config['camera_name'],
                        'hostname':
                        socket.gethostname(),
                        'moment':
                        datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                    }
                    if settings.LOCAL_TIME_FILE:
                        format_dict['timezone'] = tzctl.get_time_zone()

                    else:
                        format_dict['timezone'] = 'local time'

                    message = message % format_dict
                    subject = subject % format_dict

                    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=[])

                    settings.SMTP_TIMEOUT = old_timeout
                    logging.debug('notification email test succeeded')
                    return self.finish_json()

                except Exception as e:
                    if isinstance(e, smtplib.SMTPResponseException):
                        msg = e.smtp_error

                    else:
                        msg = str(e)

                    msg_lower = msg.lower()
                    if msg_lower.count('tls'):
                        msg = 'TLS might be required'

                    elif msg_lower.count('authentication'):
                        msg = 'authentication error'

                    elif msg_lower.count('name or service not known'):
                        msg = 'check SMTP server name'

                    elif msg_lower.count('connection refused'):
                        msg = 'check SMTP port'

                    logging.error('notification email test failed: %s' % msg,
                                  exc_info=True)
                    return self.finish_json({'error': str(msg)})

            elif what == 'telegram':
                from motioneye import sendtelegram

                logging.debug('testing telegram notification')

                try:
                    message = 'This is a test of motionEye\'s telegram messaging'
                    sendtelegram.send_message(data['api'],
                                              int(data['chatid']),
                                              message=message,
                                              files=[])

                    self.finish_json()

                    logging.debug('telegram notification test succeeded')

                except Exception as e:
                    msg = str(e)

                    msg_lower = msg.lower()
                    logging.error('telegram notification test failed: %s' %
                                  msg,
                                  exc_info=True)
                    self.finish_json({'error': str(msg)})

            elif what == 'network_share':
                logging.debug('testing access to network share //%s/%s' %
                              (data['server'], data['share']))

                try:
                    smbctl.test_share(data['server'], data['share'],
                                      data['smb_ver'], data['username'],
                                      data['password'], data['root_directory'])

                    logging.debug('access to network share //%s/%s succeeded' %
                                  (data['server'], data['share']))
                    return self.finish_json()

                except Exception as e:
                    logging.error(
                        'access to network share //%s/%s failed: %s' %
                        (data['server'], data['share'], e))
                    return self.finish_json({'error': str(e)})

            else:
                raise HTTPError(400, 'unknown test %s' % what)

        elif utils.is_remote_camera(camera_config):
            resp = await remote.test(camera_config, data)
            if resp.result is True:
                return self.finish_json()
            else:
                result = resp.result or resp.error
                return self.finish_json_with_error(result)

        else:
            raise HTTPError(400, 'cannot test features on this type of camera')
Exemplo n.º 23
0
def make_message(subject, message, camera_id, moment, timespan, callback):
    camera_config = config.get_camera(camera_id)

    # we must start the IO loop for the media list subprocess polling
    io_loop = IOLoop.instance()

    def on_media_files(media_files):
        io_loop.stop()

        timestamp = time.mktime(moment.timetuple())

        if media_files:
            logging.debug('got media files')

            # filter out non-recent media files
            media_files = [
                m for m in media_files
                if abs(m['timestamp'] - timestamp) < timespan
            ]
            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
            ]

            logging.debug('selected %d pictures' % len(media_files))

        format_dict = {
            'camera': camera_config['camera_name'],
            'hostname': socket.gethostname(),
            'moment': moment.strftime('%Y-%m-%d %H:%M:%S'),
        }

        if settings.LOCAL_TIME_FILE:
            format_dict['timezone'] = tzctl.get_time_zone()

        else:
            format_dict['timezone'] = 'local time'

        logging.debug('creating email message')

        m = message % format_dict
        s = subject % format_dict
        s = s.replace('\n', ' ')

        m += '\n\n'
        m += 'motionEye.'

        callback(s, m, media_files)

    if not timespan:
        return on_media_files([])

    logging.debug('waiting for pictures to be taken')
    time.sleep(timespan)  # give motion some time to create motion pictures

    prefix = None
    picture_filename = camera_config.get('picture_filename')
    snapshot_filename = camera_config.get('snapshot_filename')

    if ((picture_filename or snapshot_filename) and not picture_filename or
            picture_filename.startswith('%Y-%m-%d/') and not snapshot_filename
            or snapshot_filename.startswith('%Y-%m-%d/')):
        prefix = moment.strftime('%Y-%m-%d')
        logging.debug('narrowing down still images path lookup to %s' % prefix)

    fut = utils.cast_future(
        mediafiles.list_media(camera_config,
                              media_type='picture',
                              prefix=prefix))
    fut.add_done_callback(on_media_files)

    io_loop.start()
Exemplo n.º 24
0
    async def timelapse(self, camera_id, group):
        key = self.get_argument('key', None)
        check = self.get_argument('check', False)
        camera_config = config.get_camera(camera_id)

        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})

            if utils.is_local_motion_camera(camera_config):
                data = mediafiles.get_prepared_cache(key)
                if data is None:
                    logging.error('prepared cache data for key "%s" does not exist' % key)

                    raise HTTPError(404, 'no such key')

                pretty_filename = camera_config['camera_name'] + '_' + group
                pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename)
                pretty_filename += '.' + mediafiles.FFMPEG_EXT_MAPPING.get(camera_config['movie_codec'], 'avi')

                self.set_header('Content-Type', 'video/x-msvideo')
                self.set_header('Content-Disposition', 'attachment; filename=' + pretty_filename + ';')
                return self.finish(data)

            elif utils.is_remote_camera(camera_config):
                resp = await remote.get_timelapse_movie(camera_config, key, group=group)
                if resp.error:
                    msg = 'Failed to download timelapse movie from %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}

                    return self.finish_json({'error': msg})

                self.set_header('Content-Type', resp.result['content_type'])
                self.set_header('Content-Disposition', resp.result['content_disposition'])
                return self.finish(resp.result['data'])

            else:  # assuming simple mjpeg camera
                raise HTTPError(400, 'unknown operation')

        elif check:
            logging.debug('checking timelapse movie status for group "%(group)s" of camera %(id)s' % {
                'group': group or 'ungrouped', 'id': camera_id})

            if utils.is_local_motion_camera(camera_config):
                status = mediafiles.check_timelapse_movie()
                if status['progress'] == -1 and status['data']:
                    key = mediafiles.set_prepared_cache(status['data'])
                    logging.debug('prepared timelapse movie for group "%(group)s" of camera %(id)s with key %(key)s' % {
                        'group': group or 'ungrouped', 'id': camera_id, 'key': key})
                    return self.finish_json({'key': key, 'progress': -1})

                else:
                    return self.finish_json(status)

            elif utils.is_remote_camera(camera_config):
                resp = await remote.check_timelapse_movie(camera_config, group=group)
                if resp.error:
                    msg = 'Failed to check timelapse movie progress at %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': resp.error}

                    return self.finish_json({'error': msg})

                if resp.result['progress'] == -1 and resp.result.get('key'):
                    self.finish_json({'key': resp.result['key'], 'progress': -1})

                else:
                    self.finish_json(resp.result)

            else:  # assuming simple mjpeg camera
                raise HTTPError(400, 'unknown operation')

        else:  # start timelapse
            interval = int(self.get_argument('interval'))
            framerate = int(self.get_argument('framerate'))

            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:
                    return self.finish_json({'progress': status['progress']})  # timelapse already active

                else:
                    mediafiles.make_timelapse_movie(camera_config, framerate, interval, group=group)
                    return self.finish_json({'progress': -1})

            elif utils.is_remote_camera(camera_config):
                check_timelapse_resp = await remote.check_timelapse_movie(camera_config, group=group)
                if check_timelapse_resp.error:
                    return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': check_timelapse_resp.error}})

                if check_timelapse_resp.result['progress'] != -1:
                    # timelapse already active
                    return self.finish_json({'progress': check_timelapse_resp.result['progress']})

                make_timelapse_resp = await remote.make_timelapse_movie(camera_config, framerate, interval, group=group)
                if make_timelapse_resp.error:
                    return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % {
                        'url': remote.pretty_camera_url(camera_config), 'msg': make_timelapse_resp.error}})

                return self.finish_json({'progress': -1})

            else:  # assuming simple mjpeg camera
                raise HTTPError(400, 'unknown operation')