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')
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')
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()
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'))
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')
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')
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')
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)
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')
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()
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)
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)
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')
async def add_camera(self): logging.debug('adding new camera') try: device_details = json.loads(self.request.body) except Exception as e: logging.error('could not decode json: %(msg)s' % {'msg': str(e)}) raise camera_config = config.add_camera(device_details) if utils.is_local_motion_camera(camera_config): motionctl.stop() if settings.SMB_SHARES: stop, start = smbctl.update_mounts() # @UnusedVariable if start: motionctl.start() else: motionctl.start() ui_config = config.motion_camera_dict_to_ui(camera_config) return self.finish_json(ui_config) elif utils.is_remote_camera(camera_config): resp = await remote.get_config(camera_config) if resp.error: return self.finish_json_with_error(resp.error) for key, value in list(camera_config.items()): resp.remote_ui_config[key.replace('@', '')] = value return self.finish_json(resp.remote_ui_config) else: # assuming simple mjpeg camera ui_config = config.simple_mjpeg_camera_dict_to_ui(camera_config) return self.finish_json(ui_config)
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)
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)
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()
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')
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': []})
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')
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')
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}