From 6e73eef9a303087c026988b704bb888347ec0d89 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Thu, 16 Jul 2015 17:20:50 +0300 Subject: [PATCH] add camera dialog improvements --- src/handlers.py | 39 ++++------ src/remote.py | 2 +- src/server.py | 2 +- static/css/ui.css | 5 +- static/js/main.js | 195 ++++++++++++++++++++++------------------------ static/js/ui.js | 2 +- 6 files changed, 117 insertions(+), 128 deletions(-) diff --git a/src/handlers.py b/src/handlers.py index 56b99e3..2a2cfe3 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -171,11 +171,8 @@ class ConfigHandler(BaseHandler): self.get_config(camera_id) elif op == 'list': - self.list_cameras() + self.list() - elif op == 'list_devices': - self.list_devices() - elif op == 'backup': self.backup() @@ -484,7 +481,7 @@ class ConfigHandler(BaseHandler): self.finish_json({'error': True}) @BaseHandler.auth() - def list_cameras(self): + def list(self): logging.debug('listing cameras') proto = self.get_data().get('proto') @@ -497,7 +494,7 @@ class ConfigHandler(BaseHandler): cameras = [c for c in cameras if c.get('enabled')] self.finish_json({'cameras': cameras}) - remote.list_cameras(self.get_data(), on_response) + remote.list(self.get_data(), on_response) elif proto == 'netcam': def on_response(cameras=None, error=None): @@ -518,7 +515,20 @@ class ConfigHandler(BaseHandler): self.finish_json({'cameras': cameras}) utils.test_mjpeg_url(self.get_data(), auth_modes=['basic', 'digest'], allow_jpeg=False, callback=on_response) - + + elif proto == 'v4l2': + logging.debug('listing v4l2 devices') + + configured_devices = set() + for camera_id in config.get_camera_ids(): + data = config.get_camera(camera_id) + if utils.v4l2_camera(data): + configured_devices.add(data['videodevice']) + + cameras = [{'id': d[0], 'name': d[1]} for d in v4l2ctl.list_devices() if d[0] not in configured_devices] + + self.finish_json({'cameras': cameras}) + else: # assuming local motionEye camera listing cameras = [] camera_ids = config.get_camera_ids() @@ -588,21 +598,6 @@ class ConfigHandler(BaseHandler): if length[0] == 0: self.finish_json({'cameras': []}) - @BaseHandler.auth(admin=True) - def list_devices(self): - logging.debug('listing devices') - - configured_devices = {} - for camera_id in config.get_camera_ids(): - data = config.get_camera(camera_id) - if utils.v4l2_camera(data): - configured_devices[data['videodevice']] = True - - devices = [{'uri': d[0], 'name': d[1], 'configured': d[0] in configured_devices} - for d in v4l2ctl.list_devices()] - - self.finish_json({'devices': devices}) - @BaseHandler.auth(admin=True) def add_camera(self): logging.debug('adding new camera') diff --git a/src/remote.py b/src/remote.py index 0bab952..a10599a 100644 --- a/src/remote.py +++ b/src/remote.py @@ -111,7 +111,7 @@ def _remote_params(local_config): local_config.get('@remote_camera_id', local_config.get('remote_camera_id'))) -def list_cameras(local_config, callback): +def list(local_config, callback): scheme, host, port, username, password, uri, _ = _remote_params(local_config) logging.debug('listing remote cameras on %(url)s' % { diff --git a/src/server.py b/src/server.py index 9258198..623b9a4 100644 --- a/src/server.py +++ b/src/server.py @@ -43,7 +43,7 @@ application = Application( (r'^/$', handlers.MainHandler), (r'^/config/main/(?Pset|get)/?$', handlers.ConfigHandler), (r'^/config/(?P\d+)/(?Pget|set|rem|set_preview)/?$', handlers.ConfigHandler), - (r'^/config/(?Padd|list|list_devices|backup|restore)/?$', handlers.ConfigHandler), + (r'^/config/(?Padd|list|backup|restore)/?$', handlers.ConfigHandler), (r'^/picture/(?P\d+)/(?Pcurrent|list|frame)/?$', handlers.PictureHandler), (r'^/picture/(?P\d+)/(?Pdownload|preview|delete)/(?P.+?)/?$', handlers.PictureHandler), (r'^/picture/(?P\d+)/(?Pzipped|timelapse|delete_all)/(?P.+?)/?$', handlers.PictureHandler), diff --git a/static/css/ui.css b/static/css/ui.css index 3a5a2af..e3eb78c 100644 --- a/static/css/ui.css +++ b/static/css/ui.css @@ -165,8 +165,9 @@ input[type=text].number { input[type=text].error, input[type=password].error, -input[type=file].error { - background-image: url(../img/validation-error.svg); +input[type=file].error, +select.error { + background-image: url(../img/validation-error.svg) !important; background-position: center right; background-repeat: no-repeat; } diff --git a/static/js/main.js b/static/js/main.js index 290b6e0..2aab33f 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -2652,9 +2652,14 @@ function runAddCameraDialog() { var content = $('' + '' + - '' + - '' + - '' + + '' + + '' + + '' + '' + '' + '' + @@ -2671,21 +2676,21 @@ function runAddCameraDialog() { '' + '' + '' + - '' + + '' + '' + '' + '' + '' + - '' + + '' + '' + '' + - '' + + '' + '' + '' + '
Device?Camera Type?
URL?
Camera?
'); /* collect ui widgets */ - var deviceSelect = content.find('#deviceSelect'); + var typeSelect = content.find('#typeSelect'); var urlEntry = content.find('#urlEntry'); var usernameEntry = content.find('#usernameEntry'); var passwordEntry = content.find('#passwordEntry'); @@ -2696,23 +2701,22 @@ function runAddCameraDialog() { /* make validators */ makeUrlValidator(urlEntry, true); makeTextValidator(usernameEntry, false); - makeTextValidator(deviceSelect, false); + makeTextValidator(typeSelect, false); makeComboValidator(addCameraSelect, true); /* ui interaction */ function updateUi() { - content.find('tr.motioneye, tr.netcam, tr.mjpeg').css('display', 'none'); + content.find('tr.v4l2, tr.motioneye, tr.netcam, tr.mjpeg').css('display', 'none'); - if (deviceSelect.val() == 'motioneye') { + if (typeSelect.val() == 'motioneye') { content.find('tr.motioneye').css('display', 'table-row'); - addCameraSelect.hide(); usernameEntry.val('admin'); usernameEntry.attr('readonly', 'readonly'); addCameraInfo.html( 'Remote motionEye cameras are cameras installed behind another motionEye server. ' + 'Adding them here will allow you to view and manage them remotely.'); } - else if (deviceSelect.val() == 'netcam') { + else if (typeSelect.val() == 'netcam') { usernameEntry.removeAttr('readonly'); /* make sure there is one trailing slash so that @@ -2724,12 +2728,11 @@ function runAddCameraDialog() { } content.find('tr.netcam').css('display', 'table-row'); - addCameraSelect.hide(); addCameraInfo.html( 'Network cameras (or IP cameras) are devices that natively stream MJPEG videos or plain JPEG images. ' + "Consult your device's manual to find out the correct MJPEG (or JPEG) URL."); } - else if (deviceSelect.val() == 'mjpeg') { + else if (typeSelect.val() == 'mjpeg') { usernameEntry.removeAttr('readonly'); /* make sure there is one trailing slash so that @@ -2741,43 +2744,48 @@ function runAddCameraDialog() { } content.find('tr.mjpeg').css('display', 'table-row'); - addCameraSelect.hide(); addCameraInfo.html( 'Adding your device as a simple MJPEG camera instead of as a network camera will improve the framerate, ' + 'but no motion detection, picture capturing or movie recording will be available for it. ' + 'The camera must be accessible to both your server and your browser. ' + 'This type of camera is not compatible with Internet Explorer.'); } + else { /* assuming v4l2 */ + content.find('tr.v4l2').css('display', 'table-row'); + addCameraInfo.html( + 'Local cameras are camera devices that are connected directly to your motionEye system. ' + + 'These are usually USB webcams or board-specific cameras.'); + } updateModalDialogPosition(); - addCameraSelect.html(''); /* re-validate all the validators */ content.find('.validator').each(function () { this.validate(); }); - if (content.is(':visible') && uiValid() && (deviceSelect.val() in {'motioneye': 1, 'netcam': 1, 'mjpeg': 1})) { - fetchRemoteCameras(); + if (uiValid()) { + listCameras(); } } function uiValid(includeCameraSelect) { - /* re-validate all the validators */ - content.find('.validator').each(function () { - this.validate(); - }); - - var valid = true; var query = content.find('input, select'); if (!includeCameraSelect) { query = query.not('#addCameraSelect'); } else { - if (cameraMsgLabel.html()) { + if (cameraMsgLabel.html() || !addCameraSelect.val()) { return false; } } + + /* re-validate all the validators */ + content.find('.validator').each(function () { + this.validate(); + }); + + var valid = true; query.each(function () { if (this.invalid) { valid = false; @@ -2821,17 +2829,21 @@ function runAddCameraDialog() { }; } - function fetchRemoteCameras() { + function listCameras() { var progress = $('
'); + addCameraSelect.html(''); addCameraSelect.hide(); addCameraSelect.parent().find('div').remove(); /* remove any previous progress div */ addCameraSelect.before(progress); - var data = splitCameraUrl(urlEntry.val()); + var data = {}; + if (urlEntry.is(':visible') && urlEntry.val()) { + data = splitCameraUrl(urlEntry.val()); + } data.username = usernameEntry.val(); data.password = passwordEntry.val(); - data.proto = deviceSelect.val(); + data.proto = typeSelect.val(); cameraMsgLabel.html(''); @@ -2854,91 +2866,72 @@ function runAddCameraDialog() { addCameraSelect.append(''); }); + if (!data.cameras || !data.cameras.length) { + addCameraSelect.append(''); + } + addCameraSelect.show(); + addCameraSelect[0].validate(); }); } - deviceSelect.change(updateUi); + typeSelect.change(updateUi); urlEntry.change(updateUi); usernameEntry.change(updateUi); passwordEntry.change(updateUi); updateUi(); - showModalDialog(''); - - /* fetch the available devices */ - ajax('GET', baseUri + 'config/list_devices/', null, function (data) { - if (data == null || data.error) { - hideModalDialog(); - showErrorMessage(data && data.error); - return; - } - - /* add available devices */ - data.devices.forEach(function (device) { - if (!device.configured) { - deviceSelect.append(''); + runModalDialog({ + title: 'Add Camera...', + closeButton: true, + buttons: 'okcancel', + content: content, + onOk: function () { + if (!uiValid(true)) { + return false; } - }); - - deviceSelect.append(''); - deviceSelect.append(''); - deviceSelect.append(''); - - updateUi(); - - runModalDialog({ - title: 'Add Camera...', - closeButton: true, - buttons: 'okcancel', - content: content, - onOk: function () { - if (!uiValid(true)) { - return false; - } - var data = {}; - - if (deviceSelect.val() == 'motioneye') { - data = splitCameraUrl(urlEntry.val()); - data.proto = 'motioneye'; - data.username = usernameEntry.val(); - data.password = passwordEntry.val(); - data.remote_camera_id = addCameraSelect.val(); - } - else if (deviceSelect.val() == 'netcam') { - data = splitCameraUrl(urlEntry.val()); - data.username = usernameEntry.val(); - data.password = passwordEntry.val(); - data.proto = 'netcam'; - } - else if (deviceSelect.val() == 'mjpeg') { - data = splitCameraUrl(urlEntry.val()); - data.username = usernameEntry.val(); - data.password = passwordEntry.val(); - data.proto = 'mjpeg'; - } - else { /* assuming v4l2 */ - data.proto = 'v4l2'; - data.uri = deviceSelect.val(); - } - - beginProgress(); - ajax('POST', baseUri + 'config/add/', data, function (data) { - endProgress(); + var data = {}; + + if (typeSelect.val() == 'motioneye') { + data = splitCameraUrl(urlEntry.val()); + data.proto = 'motioneye'; + data.username = usernameEntry.val(); + data.password = passwordEntry.val(); + data.remote_camera_id = addCameraSelect.val(); + } + else if (typeSelect.val() == 'netcam') { + data = splitCameraUrl(urlEntry.val()); + data.username = usernameEntry.val(); + data.password = passwordEntry.val(); + data.proto = 'netcam'; + } + else if (typeSelect.val() == 'mjpeg') { + data = splitCameraUrl(urlEntry.val()); + data.username = usernameEntry.val(); + data.password = passwordEntry.val(); + data.proto = 'mjpeg'; + } + else { /* assuming v4l2 */ + data.proto = 'v4l2'; + data.uri = addCameraSelect.val(); + } + + beginProgress(); + ajax('POST', baseUri + 'config/add/', data, function (data) { + endProgress(); - if (data == null || data.error) { - showErrorMessage(data && data.error); - return; - } - - var cameraOption = $('#cameraSelect').find('option[value=add]'); - cameraOption.before(''); - $('#cameraSelect').val(data.id).change(); - recreateCameraFrames(); - }); - } - }); + if (data == null || data.error) { + showErrorMessage(data && data.error); + return; + } + + var cameraOption = $('#cameraSelect').find('option[value=add]'); + cameraOption.before(''); + $('#cameraSelect').val(data.id).change(); + recreateCameraFrames(); + }); + } }); } diff --git a/static/js/ui.js b/static/js/ui.js index 4aefc59..7dfa9bb 100644 --- a/static/js/ui.js +++ b/static/js/ui.js @@ -885,7 +885,7 @@ function makeModalDialogButtons(buttonsInfo) { var oldClick = info.click; info.click = function () { if (oldClick() == false) { - return; + return false; } hideModalDialog(); -- 2.39.5