]> www.vanbest.org Git - motioneye-debian/commitdiff
added a delete media file button
authorCalin Crisan <ccrisan@gmail.com>
Sun, 28 Sep 2014 15:46:44 +0000 (18:46 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sun, 28 Sep 2014 15:46:44 +0000 (18:46 +0300)
src/handlers.py
src/mediafiles.py
src/remote.py
src/server.py
static/css/main.css
static/js/main.js

index 87b0e0a175e00067ce1aa637bf7ebee9b5490f80..d398c745c00896f0b896ac29dc9b6329beec3e5b 100644 (file)
@@ -655,6 +655,19 @@ class PictureHandler(BaseHandler):
         else:
             raise HTTPError(400, 'unknown operation')
     
+    @asynchronous
+    def post(self, camera_id, op, filename=None, group=None):
+        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')
+        
+        if op == 'delete':
+            self.delete(camera_id, filename)
+
+        else:
+            raise HTTPError(400, 'unknown operation')
+    
     @BaseHandler.auth(prompt=False)
     def current(self, camera_id):
         self.set_header('Content-Type', 'image/jpeg')
@@ -695,9 +708,6 @@ class PictureHandler(BaseHandler):
     def list(self, camera_id):
         logging.debug('listing pictures for camera %(id)s' % {'id': 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.local_camera(camera_config):
             def on_media_list(media_list):
@@ -736,9 +746,6 @@ class PictureHandler(BaseHandler):
         logging.debug('downloading picture %(filename)s of camera %(id)s' % {
                 'filename': filename, 'id': 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.local_camera(camera_config):
             content = mediafiles.get_media_content(camera_config, filename, 'picture')
@@ -768,9 +775,6 @@ class PictureHandler(BaseHandler):
         logging.debug('previewing picture %(filename)s of camera %(id)s' % {
                 'filename': filename, 'id': 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.local_camera(camera_config):
             content = mediafiles.get_media_preview(camera_config, filename, 'picture',
@@ -802,10 +806,31 @@ class PictureHandler(BaseHandler):
                     height=self.get_argument('height', None))
     
     @BaseHandler.auth()
-    def zipped(self, camera_id, group):
-        if camera_id not in config.get_camera_ids():
-            raise HTTPError(404, 'no such camera')
+    def delete(self, camera_id, filename):
+        logging.debug('deleting picture %(filename)s of camera %(id)s' % {
+                'filename': filename, 'id': camera_id})
+        
+        camera_config = config.get_camera(camera_id)
+        if utils.local_camera(camera_config):
+            try:
+                mediafiles.del_media_content(camera_config, filename, 'picture')
+                self.finish_json()
+                
+            except Exception as e:
+                self.finish_json({'error': unicode(e)})
+
+        else: # remote camera
+            def on_response(response=None, error=None):
+                if error:
+                    return self.finish_json({'error': 'Failed to delete picture from %(url)s: %(msg)s.' % {
+                            'url': remote.make_camera_url(camera_config)}, 'msg': error})
+
+                self.finish_json()
 
+            remote.del_media_content(camera_config, on_response, filename=filename, media_type='picture')
+
+    @BaseHandler.auth()
+    def zipped(self, camera_id, group):
         key = self.get_argument('key', None)
         if key:
             logging.debug('serving zip file for group %(group)s of camera %(id)s with key %(key)s' % {
@@ -861,9 +886,6 @@ class PictureHandler(BaseHandler):
 
     @BaseHandler.auth()
     def timelapse(self, camera_id, group):
-        if camera_id not in config.get_camera_ids():
-            raise HTTPError(404, 'no such camera')
-
         key = self.get_argument('key', None)
         if key:
             logging.debug('serving timelapse movie for group %(group)s of camera %(id)s with key %(key)s' % {
@@ -948,13 +970,23 @@ class MovieHandler(BaseHandler):
         else:
             raise HTTPError(400, 'unknown operation')
     
+    @asynchronous
+    def post(self, camera_id, op, filename=None):
+        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')
+        
+        if op == 'delete':
+            self.delete(camera_id, filename)
+        
+        else:
+            raise HTTPError(400, 'unknown operation')
+    
     @BaseHandler.auth()
     def list(self, camera_id):
         logging.debug('listing movies for camera %(id)s' % {'id': 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.local_camera(camera_config):
             def on_media_list(media_list):
@@ -984,9 +1016,6 @@ class MovieHandler(BaseHandler):
         logging.debug('downloading movie %(filename)s of camera %(id)s' % {
                 'filename': filename, 'id': 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.local_camera(camera_config):
             content = mediafiles.get_media_content(camera_config, filename, 'movie')
@@ -1016,9 +1045,6 @@ class MovieHandler(BaseHandler):
         logging.debug('previewing movie %(filename)s of camera %(id)s' % {
                 'filename': filename, 'id': 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.local_camera(camera_config):
             content = mediafiles.get_media_preview(camera_config, filename, 'movie',
@@ -1049,6 +1075,29 @@ class MovieHandler(BaseHandler):
                     width=self.get_argument('width', None),
                     height=self.get_argument('height', None))
 
+    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.local_camera(camera_config):
+            try:
+                mediafiles.del_media_content(camera_config, filename, 'movie')
+                self.finish_json()
+                
+            except Exception as e:
+                self.finish_json({'error': unicode(e)})
+
+        else: # remote camera
+            def on_response(response=None, error=None):
+                if error:
+                    return self.finish_json({'error': 'Failed to delete movie from %(url)s: %(msg)s.' % {
+                            'url': remote.make_camera_url(camera_config)}, 'msg': error})
+
+                self.finish_json()
+
+            remote.del_media_content(camera_config, on_response, filename=filename, media_type='movie')
+
 
 class UpdateHandler(BaseHandler):
     @BaseHandler.auth(admin=True)
index 7d798b19eafcdeda6797a085f2cbf77df29b8ee5..dc7e155ecb1a710ad0904bc90c4e497bcc590a15 100644 (file)
@@ -574,6 +574,21 @@ def get_media_preview(camera_config, path, media_type, width, height):
     return sio.getvalue()
 
 
+def del_media_content(camera_config, path, media_type):
+    target_dir = camera_config.get('target_dir')
+
+    full_path = os.path.join(target_dir, path)
+    
+    try:
+        os.remove(full_path)
+    
+    except Exception as e:
+        logging.error('failed to read file %(path)s: %(msg)s' % {
+                'path': full_path, 'msg': unicode(e)})
+        
+        raise
+
+
 def get_current_picture(camera_config, width, height):
     import mjpgclient
 
index 32abab10e7ab956020c6f22e679345b4aca34492..fb0812f9a0c682a0e0144e4328d98bbb53bac0fe 100644 (file)
@@ -517,3 +517,39 @@ def get_media_preview(local_config, callback, filename, media_type, width, heigh
 
     http_client = AsyncHTTPClient()
     http_client.fetch(request, on_response)
+
+
+def del_media_content(local_config, callback, filename, media_type):
+    host = local_config.get('@host', local_config.get('host'))
+    port = local_config.get('@port', local_config.get('port'))
+    username = local_config.get('@username', local_config.get('username'))
+    password = local_config.get('@password', local_config.get('password'))
+    uri = local_config.get('@uri', local_config.get('uri')) or ''
+    camera_id = local_config.get('@remote_camera_id', local_config.get('remote_camera_id'))
+    
+    logging.debug('deleting file %(filename)s of remote camera %(id)s on %(url)s' % {
+            'filename': filename,
+            'id': camera_id,
+            'url': make_camera_url(local_config)})
+    
+    uri += '/%(media_type)s/%(id)s/delete/%(filename)s' % {
+            'media_type': media_type,
+            'id': camera_id,
+            'filename': filename}
+
+    request = _make_request(host, port, username, password, uri, method='POST', timeout=settings.REMOTE_REQUEST_TIMEOUT)
+
+    def on_response(response):
+        if response.error:
+            logging.error('failed to delete file %(filename)s of remote camera %(id)s on %(url)s: %(msg)s' % {
+                    'filename': filename,
+                    'id': camera_id,
+                    'url': make_camera_url(local_config),
+                    'msg': unicode(response.error)})
+            
+            return callback(error=unicode(response.error))
+        
+        return callback()
+
+    http_client = AsyncHTTPClient()
+    http_client.fetch(request, on_response)
index f42758c0f2cfea8a3a4955e5ecc6a180e84eb52f..5a00a365028ef88c12b1729900dab21566f19eaf 100644 (file)
@@ -45,10 +45,10 @@ application = Application(
         (r'^/config/(?P<camera_id>\d+)/(?P<op>get|set|rem|set_preview)/?$', handlers.ConfigHandler),
         (r'^/config/(?P<op>add|list|list_devices)/?$', 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)/(?P<filename>.+?)/?$', 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)/(?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)/(?P<filename>.+?)/?$', handlers.MovieHandler),
+        (r'^/movie/(?P<camera_id>\d+)/(?P<op>download|preview|delete)/(?P<filename>.+?)/?$', handlers.MovieHandler),
         (r'^/update/?$', handlers.UpdateHandler),
         (r'^/power/(?P<op>shutdown)/?$', handlers.PowerHandler),
         (r'^/version/?$', handlers.VersionHandler),
index ca4897d402f359b4d9c7c84aea6d85985c2e4b43..27b59a2f5213634e9428fd8ef52a78324471b91a 100644 (file)
@@ -580,19 +580,27 @@ div.media-list-entry-details span.details-moment-short {
     display: none;
 }
 
-div.media-list-download-button {
+div.media-list-download-button,
+div.media-list-delete-button {
     float: right;
+    clear: right;
     height: 1.5em;
+    width: 5em;
     line-height: 1.5em;
     text-align: center;
-    margin: 1.2em 0.5em;
+    margin: 0px 0.5em;
     padding: 0px 0.5em;
     color: white;
-    background-color: #317CAD;
     border-radius: 3px;
     transition: all 0.1s linear;
 }
 
+div.media-list-download-button {
+    margin-top: 0.4em;
+    margin-bottom: 0.1em;
+    background: #317CAD;
+}
+
 div.media-list-download-button:HOVER {
     background-color: #3498db;
 }
@@ -601,6 +609,20 @@ div.media-list-download-button:ACTIVE {
     background-color: #317CAD;
 }
 
+div.media-list-delete-button {
+    margin-top: 0.1em;
+    margin-bottom: 0.4em;
+    background: #c0392b;
+}
+
+div.media-list-delete-button:HOVER {
+    background-color: #D43F2F;
+}
+
+div.media-list-delete-button:ACTIVE {
+    background-color: #B03427;
+}
+
 div.media-dialog-buttons {
     margin: 0.5em 0px 0px 0px;
     text-align: center;
index 4b3fd158d60c67ba22ae2ed62e3b0a4bf7b857be..da413b9545d7e30bc8cbfffdec2ee0f92f01289a 100644 (file)
@@ -1312,6 +1312,21 @@ function doDownloadZipped(cameraId, groupKey) {
     });
 }
 
+function doDeleteFile(uri, callback) {
+    var url = window.location.href;
+    var parts = url.split('/');
+    url = parts.slice(0, 3).join('/') + uri;
+    
+    runConfirmDialog('Really delete this file?', function () {
+        ajax('POST', url, null, function () {
+            if (callback) {
+                callback();
+            }
+        });
+    }, {stack: true});
+}
+
+
 
     /* fetch & push */
 
@@ -1502,12 +1517,32 @@ function getCameraIdsByInstance() {
 
     /* dialogs */
 
-function runAlertDialog(message, onOk) {
-    runModalDialog({title: message, buttons: 'ok', onOk: onOk});
+function runAlertDialog(message, onOk, options) {
+    var params = {
+        title: message,
+        buttons: 'ok',
+        onOk: onOk
+    };
+    
+    if (options) {
+        Object.update(params, options);
+    }
+    
+    runModalDialog(params);
 }
 
-function runConfirmDialog(message, onYes) {
-    runModalDialog({title: message, buttons: 'yesno', onYes: onYes});
+function runConfirmDialog(message, onYes, options) {
+    var params = {
+        title: message,
+        buttons: 'yesno',
+        onYes: onYes
+    };
+    
+    if (options) {
+        Object.update(params, options);
+    }
+    
+    runModalDialog(params);
 }
 
 function runPictureDialog(entries, pos, mediaType) {
@@ -2074,6 +2109,12 @@ function runMediaDialog(cameraId, mediaType) {
                         var downloadButton = $('<div class="media-list-download-button button">Download</div>');
                         entryDiv.append(downloadButton);
                         
+                        var deleteButton = $('<div class="media-list-delete-button button">Delete</div>');
+                        
+                        if (user === 'admin') {
+                            entryDiv.append(deleteButton);
+                        }
+
                         var nameDiv = $('<div class="media-list-entry-name">' + entry.name + '</div>');
                         entryDiv.append(nameDiv);
                         
@@ -2081,10 +2122,18 @@ function runMediaDialog(cameraId, mediaType) {
                         entryDiv.append(detailsDiv);
                         
                         downloadButton.click(function () {
-                            downloadFile('/picture/' + cameraId + '/download' + entry.path);
+                            downloadFile('/' + mediaType + '/' + cameraId + '/download' + entry.path);
                             return false;
                         });
                         
+                        deleteButton.click(function () {
+                            doDeleteFile('/' + mediaType + '/' + cameraId + '/delete' + entry.path, function () {
+                                entryDiv.remove();
+                            });
+                            
+                            return false;
+                        });
+
                         entryDiv.click(function () {
                             var pos = entries.indexOf(entry);
                             runPictureDialog(entries, pos, mediaType);