From a13c150212993a80bbc525e447dd7e6525acb05a Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 29 Sep 2013 22:01:53 +0300 Subject: [PATCH] added motion restarting mechanism --- doc/todo.txt | 1 + motioneye.py | 23 +++++------ src/config.py | 3 +- src/handlers.py | 68 ++++++++++++++++++--------------- static/css/main.css | 17 +++++++++ static/img/camera-progress.gif | Bin 0 -> 3251 bytes static/js/main.js | 12 ++++++ 7 files changed, 82 insertions(+), 42 deletions(-) create mode 100644 static/img/camera-progress.gif diff --git a/doc/todo.txt b/doc/todo.txt index d1032ac..0e36e92 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,6 +1,7 @@ -> camera not available background and icon design -> remove current snapshot GET logs -> a nice shadow behind the header +-> add a motion running status indicator (and maybe a start/stop button) -> group @config rules to top -> browser compatibility test diff --git a/motioneye.py b/motioneye.py index ad239fb..9eb0d0c 100644 --- a/motioneye.py +++ b/motioneye.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 import logging +import motionctl import os.path import signal import sys @@ -22,6 +23,10 @@ def _configure_signals(): ioloop.stop() logging.info('server stopped') + + if motionctl.running(): + motionctl.stop() + logging.info('motion stopped') signal.signal(signal.SIGINT, bye_handler) signal.signal(signal.SIGTERM, bye_handler) @@ -40,18 +45,14 @@ def _start_server(): tornado.ioloop.IOLoop.instance().start() +def _start_motion(): + if not motionctl.running(): + motionctl.start() + logging.info('motion started') + + if __name__ == '__main__': _configure_signals() _configure_logging() + _start_motion() _start_server() - -# import config # TODO remove this -# main_config = config.get_main() -# config.add_camera('v4l2:///dev/video0') -# #data = config.get_camera(1) -# #data['@enabled'] = True -# #config.set_camera(1, data) -# config.rem_camera(1) - -# import motionctl -# motionctl.start() diff --git a/src/config.py b/src/config.py index 8e3c617..262ec4e 100644 --- a/src/config.py +++ b/src/config.py @@ -509,8 +509,9 @@ def _set_default_motion_camera(data): data.setdefault('quality', 75) data.setdefault('@preserve_images', 0) - data.setdefault('movie_filename', '') data.setdefault('ffmpeg_variable_bitrate', 14) + data.setdefault('movie_filename', '') + data.setdefault('ffmpeg_cap_new', False) data.setdefault('@preserve_movies', 0) data.setdefault('@motion_notifications', False) diff --git a/src/handlers.py b/src/handlers.py index 17f9e8f..c0cd554 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -6,6 +6,7 @@ from tornado.web import RequestHandler, HTTPError import config import mjpgclient +import motionctl import template import v4l2ctl @@ -111,30 +112,42 @@ class ConfigHandler(BaseHandler): raise - if camera_id: - logging.debug('setting config for camera %(id)s' % {'id': camera_id}) - - camera_ids = config.get_camera_ids() - if camera_id not in camera_ids: - raise HTTPError(404, 'no such camera') - - data = self._camera_ui_to_dict(data) - config.set_camera(camera_id, data) - - else: - logging.debug('setting main config') - - try: - data = json.loads(self.request.body) - - except Exception as e: - logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)}) + restart = bool(data.get('restart')) + if restart and motionctl.running(): + motionctl.stop() + + try: + if camera_id: + logging.debug('setting config for camera %(id)s' % {'id': camera_id}) - raise - - data = self._main_ui_to_dict(data) - config.set_main(data) + camera_ids = config.get_camera_ids() + if camera_id not in camera_ids: + raise HTTPError(404, 'no such camera') + + data = self._camera_ui_to_dict(data) + config.set_camera(camera_id, data) + else: + logging.debug('setting main config') + + try: + data = json.loads(self.request.body) + + except Exception as e: + logging.error('could not decode json: %(msg)s' % {'msg': unicode(e)}) + + raise + + data = self._main_ui_to_dict(data) + config.set_main(data) + + except: + raise + + finally: + if restart: + motionctl.start() + def set_preview(self, camera_id): try: controls = json.loads(self.request.body) @@ -276,10 +289,9 @@ class ConfigHandler(BaseHandler): '@preserve_images': int(ui.get('preserve_images', 0)), # movies - 'ffmpeg_variable_bitrate': 0, - 'ffmpeg_video_codec': 'mpeg4', - 'ffmpeg_cap_new': True, - 'movie_filename': '', + 'ffmpeg_variable_bitrate': 2 + int((100 - int(ui.get('movie_quality', 75))) * 0.29), + 'ffmpeg_cap_new': ui.get('motion_movies', False), + 'movie_filename': ui.get('movie_file_name', '%Y-%m-%d-%H-%M-%S-%q'), '@preserve_movies': int(ui.get('preserve_movies', 0)), # motion detection @@ -341,10 +353,6 @@ class ConfigHandler(BaseHandler): data['quality'] = max(1, int(ui.get('image_quality', 75))) - if ui.get('motion_movies', False): - data['ffmpeg_variable_bitrate'] = 2 + int((100 - int(ui.get('movie_quality', 75))) * 0.29) - data['movie_filename'] = ui.get('movie_file_name', '%Y-%m-%d-%H-%M-%S-%q') - if ui.get('working_schedule', False): data['@working_schedule'] = ( ui.get('monday_from', '') + '-' + ui.get('monday_to') + '|' + diff --git a/static/css/main.css b/static/css/main.css index efe24fc..d4b21e0 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -353,6 +353,23 @@ div.camera-placeholder { background-color: #555; } +div.camera-progress { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + opacity: 0; + transition: all 0.2s linear; + text-align: center; +} + +img.camera-progress { + margin-top: 28%; + border: 10px solid white; + border-radius: 10px; +} + /* media queries */ diff --git a/static/img/camera-progress.gif b/static/img/camera-progress.gif new file mode 100644 index 0000000000000000000000000000000000000000..023347e4680b09d593822759b1b54a923abf320a GIT binary patch literal 3251 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 literal 0 HcmV?d00001 diff --git a/static/js/main.js b/static/js/main.js index c6ae76f..70e6ee5 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -618,6 +618,9 @@ function showProgress() { applyButton.css('display', 'inline-block'); applyButton.animate({'opacity': '1'}, 100); applyButton.addClass('progress'); + + $('img.camera').css('opacity', '0.3'); + $('div.camera-progress').css('opacity', '0.5'); } function hideApply() { @@ -640,6 +643,9 @@ function endProgress() { else { showApply(); } + + $('div.camera-progress').css('opacity', '0'); + $('img.camera').css('opacity', '1'); } function isProgress() { @@ -679,6 +685,9 @@ function doApply() { for (var i = 0; i < configs.length; i++) { var config = configs[i]; + if (i === configs.length - 1) { + config.config['restart'] = true; + } ajax('POST', '/config/' + config.key + '/set/', config.config, function () { finishedCount++; testReady(); @@ -785,6 +794,7 @@ function addCameraFrameUi(cameraId, cameraName, framerate) { '
' + '
' + '' + + '
' + '
' + ''); @@ -792,11 +802,13 @@ function addCameraFrameUi(cameraId, cameraName, framerate) { var configureButton = cameraFrameDiv.find('div.camera-button.configure'); var closeButton = cameraFrameDiv.find('div.camera-button.close'); var cameraImg = cameraFrameDiv.find('img.camera'); + var progressImg = cameraFrameDiv.find('img.camera-progress'); cameraFrameDiv.attr('id', 'camera' + cameraId); cameraFrameDiv[0].framerate = framerate; cameraFrameDiv[0].refreshDivider = 0; nameSpan.html(cameraName); + progressImg.attr('src', staticUrl + 'img/camera-progress.gif'); /* insert the new camera frame at the right position, * with respect to the camera id */ -- 2.39.5