From 075c25d93d455088c8004783ced27098664ec198 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Nov 2015 17:35:52 +0200 Subject: [PATCH] fixed various bugs dealing with ungrouped media files --- motioneye/handlers.py | 46 ++++++----- motioneye/remote.py | 24 +++--- motioneye/server.py | 4 +- motioneye/static/js/main.js | 151 ++++++++++++++++++++++++------------ 4 files changed, 143 insertions(+), 82 deletions(-) diff --git a/motioneye/handlers.py b/motioneye/handlers.py index 69f8912..e1debcf 100644 --- a/motioneye/handlers.py +++ b/motioneye/handlers.py @@ -726,6 +726,9 @@ class PictureHandler(BaseHandler): @asynchronous def post(self, camera_id, op, filename=None, group=None): + if group == '/': # ungrouped + group = '' + if camera_id is not None: camera_id = int(camera_id) if camera_id not in config.get_camera_ids(): @@ -935,8 +938,8 @@ class PictureHandler(BaseHandler): 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, 'id': camera_id, 'key': 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.local_motion_camera(camera_config): data = mediafiles.get_prepared_cache(key) @@ -968,8 +971,8 @@ class PictureHandler(BaseHandler): raise HTTPError(400, 'unknown operation') else: # prepare - logging.debug('preparing zip file for group %(group)s of camera %(id)s' % { - 'group': group, 'id': camera_id}) + logging.debug('preparing zip file for group "%(group)s" of camera %(id)s' % { + 'group': group or 'ungrouped', 'id': camera_id}) if utils.local_motion_camera(camera_config): def on_zip(data): @@ -977,8 +980,8 @@ class PictureHandler(BaseHandler): 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, 'id': camera_id, 'key': key}) + 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}) mediafiles.get_zipped_content(camera_config, media_type='picture', group=group, callback=on_zip) @@ -1003,8 +1006,8 @@ class PictureHandler(BaseHandler): 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, 'id': camera_id, 'key': key}) + 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.local_motion_camera(camera_config): data = mediafiles.get_prepared_cache(key) @@ -1036,15 +1039,15 @@ class PictureHandler(BaseHandler): raise HTTPError(400, 'unknown operation') elif check: - logging.debug('checking timelapse movie status for group %(group)s of camera %(id)s' % { - 'group': group, 'id': camera_id}) + logging.debug('checking timelapse movie status for group "%(group)s" of camera %(id)s' % { + 'group': group or 'ungrouped', 'id': camera_id}) if utils.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, 'id': camera_id, 'key': key}) + 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}) self.finish_json({'key': key, 'progress': -1}) else: @@ -1071,8 +1074,8 @@ class PictureHandler(BaseHandler): interval = int(self.get_argument('interval')) framerate = int(self.get_argument('framerate')) - logging.debug('preparing timelapse movie for group %(group)s of camera %(id)s with rate %(framerate)s/%(int)s' % { - 'group': group, 'id': camera_id, 'framerate': framerate, 'int': interval}) + logging.debug('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}) if utils.local_motion_camera(camera_config): status = mediafiles.check_timelapse_movie() @@ -1108,8 +1111,8 @@ class PictureHandler(BaseHandler): @BaseHandler.auth(admin=True) def delete_all(self, camera_id, group): - logging.debug('deleting picture group %(group)s of camera %(id)s' % { - 'group': group, 'id': camera_id}) + logging.debug('deleting picture group "%(group)s" of camera %(id)s' % { + 'group': group or 'ungrouped', 'id': camera_id}) camera_config = config.get_camera(camera_id) if utils.local_motion_camera(camera_config): @@ -1123,7 +1126,7 @@ class PictureHandler(BaseHandler): elif utils.remote_camera(camera_config): def on_response(response=None, error=None): if error: - return self.finish_json({'error': 'Failed to delete picture group from %(url)s: %(msg)s.' % { + return self.finish_json({'error': 'Failed to delete picture group at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) self.finish_json() @@ -1163,6 +1166,9 @@ class MovieHandler(BaseHandler): @asynchronous def post(self, camera_id, op, filename=None, group=None): + if group == '/': # ungrouped + group = '' + if camera_id is not None: camera_id = int(camera_id) if camera_id not in config.get_camera_ids(): @@ -1308,8 +1314,8 @@ class MovieHandler(BaseHandler): @BaseHandler.auth(admin=True) def delete_all(self, camera_id, group): - logging.debug('deleting movie group %(group)s of camera %(id)s' % { - 'group': group, 'id': camera_id}) + 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.local_motion_camera(camera_config): @@ -1323,7 +1329,7 @@ class MovieHandler(BaseHandler): elif utils.remote_camera(camera_config): def on_response(response=None, error=None): if error: - return self.finish_json({'error': 'Failed to delete movie group from %(url)s: %(msg)s.' % { + return self.finish_json({'error': 'Failed to delete movie group at %(url)s: %(msg)s.' % { 'url': remote.pretty_camera_url(camera_config), 'msg': error}}) self.finish_json() diff --git a/motioneye/remote.py b/motioneye/remote.py index 9533cbc..de4daf4 100644 --- a/motioneye/remote.py +++ b/motioneye/remote.py @@ -366,8 +366,8 @@ def get_media_content(local_config, filename, media_type, callback): def make_zipped_content(local_config, media_type, group, callback): scheme, host, port, username, password, path, camera_id = _remote_params(local_config) - logging.debug('preparing zip file for group %(group)s of remote camera %(id)s on %(url)s' % { - 'group': group, + logging.debug('preparing zip file for group "%(group)s" of remote camera %(id)s on %(url)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config)}) @@ -381,8 +381,8 @@ def make_zipped_content(local_config, media_type, group, callback): def on_response(response): if response.error: - logging.error('failed to prepare zip file for group %(group)s of remote camera %(id)s on %(url)s: %(msg)s' % { - 'group': group, + logging.error('failed to prepare zip file for group "%(group)s" of remote camera %(id)s on %(url)s: %(msg)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config), 'msg': utils.pretty_http_error(response)}) @@ -441,8 +441,8 @@ def get_zipped_content(local_config, media_type, key, group, callback): def make_timelapse_movie(local_config, framerate, interval, group, callback): scheme, host, port, username, password, path, camera_id = _remote_params(local_config) - logging.debug('making timelapse movie for group %(group)s of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s' % { - 'group': group, + logging.debug('making timelapse movie for group "%(group)s" of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'framerate': framerate, 'int': interval, @@ -458,8 +458,8 @@ def make_timelapse_movie(local_config, framerate, interval, group, callback): def on_response(response): if response.error: - logging.error('failed to make timelapse movie for group %(group)s of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s: %(msg)s' % { - 'group': group, + logging.error('failed to make timelapse movie for group "%(group)s" of remote camera %(id)s with rate %(framerate)s/%(int)s on %(url)s: %(msg)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config), 'int': interval, @@ -625,8 +625,8 @@ def del_media_content(local_config, filename, media_type, callback): def del_media_group(local_config, group, media_type, callback): scheme, host, port, username, password, path, camera_id = _remote_params(local_config) - logging.debug('deleting group %(group)s of remote camera %(id)s on %(url)s' % { - 'group': group, + logging.debug('deleting group "%(group)s" of remote camera %(id)s on %(url)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config)}) @@ -639,8 +639,8 @@ def del_media_group(local_config, group, media_type, callback): def on_response(response): if response.error: - logging.error('failed to delete group %(group)s of remote camera %(id)s on %(url)s: %(msg)s' % { - 'group': group, + logging.error('failed to delete group "%(group)s" of remote camera %(id)s on %(url)s: %(msg)s' % { + 'group': group or 'ungrouped', 'id': camera_id, 'url': pretty_camera_url(local_config), 'msg': utils.pretty_http_error(response)}) diff --git a/motioneye/server.py b/motioneye/server.py index e2a3eab..b2eaf83 100644 --- a/motioneye/server.py +++ b/motioneye/server.py @@ -166,10 +166,10 @@ handler_mapping = [ (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), + (r'^/picture/(?P\d+)/(?Pzipped|timelapse|delete_all)/(?P.*?)/?$', handlers.PictureHandler), (r'^/movie/(?P\d+)/(?Plist)/?$', handlers.MovieHandler), (r'^/movie/(?P\d+)/(?Pdownload|preview|delete)/(?P.+?)/?$', handlers.MovieHandler), - (r'^/movie/(?P\d+)/(?Pdelete_all)/(?P.+?)/?$', handlers.MovieHandler), + (r'^/movie/(?P\d+)/(?Pdelete_all)/(?P.*?)/?$', handlers.MovieHandler), (r'^/_relay_event/?$', handlers.RelayEventHandler), (r'^/log/(?P\w+)/?$', handlers.LogHandler), (r'^/update/?$', handlers.UpdateHandler), diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index ac361fd..c34b5a2 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -258,6 +258,59 @@ function ajax(method, url, data, callback, error, timeout) { $.ajax(options); } +function getCookie(name) { + if (document.cookie.length <= 0) { + return null; + } + + var start = document.cookie.indexOf(name + '='); + if (start == -1) { + return null; + } + + var start = start + name.length + 1; + var end = document.cookie.indexOf(';', start); + if (end == -1) { + end = document.cookie.length; + } + + return unescape(document.cookie.substring(start, end)); +} + +function setCookie(name, value, days) { + var date, expires; + if (days) { + date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = 'expires=' + date.toGMTString(); + } + else { + expires = ''; + } + + document.cookie = name + '=' + value + '; ' + expires + '; path=/'; +} + +function remCookie(name) { + document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; +} + +function showErrorMessage(message) { + if (message == null || message == true) { + message = 'An error occurred. Refreshing is recommended.'; + } + + showPopupMessage(message, 'error'); +} + +function doLogout() { + setCookie('username', '_'); + window.location.reload(true); +} + + + /* Object utilities */ + Object.keys = Object.keys || (function () { var hasOwnProperty = Object.prototype.hasOwnProperty; var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'); @@ -306,6 +359,9 @@ Object.update = function (dest, source) { } }; + + /* Array utilities */ + Array.prototype.indexOf = Array.prototype.indexOf || function (obj) { for (var i = 0; i < this.length; i++) { if (this[i] === obj) { @@ -390,6 +446,9 @@ Array.prototype.sortKey = function (keyFunc, reverse) { }); }; + + /* String utilities */ + String.prototype.startsWith = String.prototype.startsWith || function (str) { return (this.substr(0, str.length) === str); }; @@ -411,58 +470,33 @@ String.prototype.replaceAll = String.prototype.replaceAll || function (oldStr, n return s.toString(); }; -function getCookie(name) { - if (document.cookie.length <= 0) { - return null; - } - - var start = document.cookie.indexOf(name + '='); - if (start == -1) { - return null; - } - - var start = start + name.length + 1; - var end = document.cookie.indexOf(';', start); - if (end == -1) { - end = document.cookie.length; - } +String.prototype.format = function () { + var text = this; - return unescape(document.cookie.substring(start, end)); -} - -function setCookie(name, value, days) { - var date, expires; - if (days) { - date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = 'expires=' + date.toGMTString(); + var rex = new RegExp('%[sdf]'); + var match, i = 0; + while (match = text.match(rex)) { + text = text.substring(0, match.index) + arguments[i] + text.substring(match.index + 2); + i++; } - else { - expires = ''; + + if (i) { /* %s format used */ + return text; } - - document.cookie = name + '=' + value + '; ' + expires + '; path=/'; -} - -function remCookie(name) { - document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; -} - -function showErrorMessage(message) { - if (message == null || message == true) { - message = 'An error occurred. Refreshing is recommended.'; + + var keywords = arguments[0]; + + for (var key in keywords) { + text = text.replace('%(' + key + ')s', "" + keywords[key]); + text = text.replace('%(' + key + ')d', "" + keywords[key]); + text = text.replace('%(' + key + ')f', "" + keywords[key]); } - showPopupMessage(message, 'error'); -} - -function doLogout() { - setCookie('username', '_'); - window.location.reload(true); -} - + return text; +}; -/* UI initialization */ + + /* UI initialization */ function initUI() { /* checkboxes */ @@ -2276,9 +2310,30 @@ function doDeleteFile(path, callback) { } function doDeleteAllFiles(mediaType, cameraId, groupKey, callback) { - runConfirmDialog('Really delete all ' + mediaType + 's in ' + groupKey + '?', function () { + var msg; + if (groupKey) { + if (mediaType == 'picture') { + msg = 'Really delete all pictures from "%(group)s"?'.format({group: groupKey}); + } + else { + msg = 'Really delete all movies from "%(group)s"?'.format({group: groupKey}); + } + } + else { + if (mediaType == 'picture') { + msg = 'Really delete all ungrouped pictures?'; + } + else { + msg = 'Really delete all ungrouped movies?'; + } + } + + runConfirmDialog(msg, function () { showModalDialog('', null, null, true); - ajax('POST', basePath + mediaType + '/' + cameraId + '/delete_all/' + groupKey + '/', null, function (data) { + if (groupKey) { + groupKey += '/'; + } + ajax('POST', basePath + mediaType + '/' + cameraId + '/delete_all/' + groupKey, null, function (data) { hideModalDialog(); /* progress */ hideModalDialog(); /* confirm */ -- 2.39.5