fixed various bugs dealing with ungrouped media files
authorCalin Crisan <ccrisan@gmail.com>
Sun, 1 Nov 2015 15:35:52 +0000 (17:35 +0200)
committerCalin Crisan <ccrisan@gmail.com>
Sun, 1 Nov 2015 15:35:52 +0000 (17:35 +0200)
motioneye/handlers.py
motioneye/remote.py
motioneye/server.py
motioneye/static/js/main.js

index 69f89127adc43d6273cd159ba726218728898ed8..e1debcf25115d29781101c380fcef759aa701ef4 100644 (file)
@@ -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()
index 9533cbc015823f4d1e34a81c8b671e92fd096b8e..de4daf4c1411b05109006cec8e094ae33b846318 100644 (file)
@@ -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)})
index e2a3eabe6a2c105a99f363dc7a5010867e9f221d..b2eaf83becf0e552bd19145b72d84cdb3d66efbe 100644 (file)
@@ -166,10 +166,10 @@ handler_mapping = [
     (r'^/config/(?P<op>add|list|backup|restore)/?$', handlers.ConfigHandler),
     (r'^/picture/(?P<camera_id>\d+)/(?P<op>current|list|frame)/?$', handlers.PictureHandler),
     (r'^/picture/(?P<camera_id>\d+)/(?P<op>download|preview|delete)/(?P<filename>.+?)/?$', handlers.PictureHandler),
-    (r'^/picture/(?P<camera_id>\d+)/(?P<op>zipped|timelapse|delete_all)/(?P<group>.+?)/?$', handlers.PictureHandler),
+    (r'^/picture/(?P<camera_id>\d+)/(?P<op>zipped|timelapse|delete_all)/(?P<group>.*?)/?$', handlers.PictureHandler),
     (r'^/movie/(?P<camera_id>\d+)/(?P<op>list)/?$', handlers.MovieHandler),
     (r'^/movie/(?P<camera_id>\d+)/(?P<op>download|preview|delete)/(?P<filename>.+?)/?$', handlers.MovieHandler),
-    (r'^/movie/(?P<camera_id>\d+)/(?P<op>delete_all)/(?P<group>.+?)/?$', handlers.MovieHandler),
+    (r'^/movie/(?P<camera_id>\d+)/(?P<op>delete_all)/(?P<group>.*?)/?$', handlers.MovieHandler),
     (r'^/_relay_event/?$', handlers.RelayEventHandler),
     (r'^/log/(?P<name>\w+)/?$', handlers.LogHandler),
     (r'^/update/?$', handlers.UpdateHandler),
index ac361fd78b054697d73c23ac03ed3074a09c989f..c34b5a250f86b4394a011108bb631589e132ad7c 100644 (file)
@@ -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('<div class="modal-progress"></div>', 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 */