]> www.vanbest.org Git - motioneye-debian/commitdiff
media files are now made of *pictures* and *movies*
authorCalin Crisan <ccrisan@gmail.com>
Sat, 9 Nov 2013 18:12:10 +0000 (20:12 +0200)
committerCalin Crisan <ccrisan@gmail.com>
Sat, 9 Nov 2013 18:12:10 +0000 (20:12 +0200)
doc/todo.txt
motioneye.py
settings_default.py
src/cleanup.py [deleted file]
src/config.py
src/handlers.py
src/mediafiles.py [new file with mode: 0644]
src/remote.py
src/server.py
static/js/main.js
templates/main.html

index dc1a2ac9cd5c1592fb2a687ef1e1885360fd188f..e7968cd11f0689b426c32518d5ba8a6e800f9602 100644 (file)
@@ -6,6 +6,7 @@
 -> style scroll bars
 -> hint text next to section titles
 -> clickable hints
+-> update mechanism should not say "ok" until the server reloads
 
 -> implement working schedule (add another combo deciding what to do during working schedule)
 -> implement notifications
index 911b488780905cd9b35cd5c3c65f71da058be658..f677f242aa7fab3d88daecb9b04a12a4493d5ac9 100755 (executable)
@@ -178,7 +178,7 @@ def _start_motion():
 
 
 def _start_cleanup():
-    import cleanup
+    import mediafiles
 
     def do_cleanup():
         ioloop = tornado.ioloop.IOLoop.instance()
@@ -186,8 +186,8 @@ def _start_cleanup():
             return
         
         try:
-            cleanup.cleanup_images()
-            cleanup.cleanup_movies()
+            mediafiles.cleanup_pictures()
+            mediafiles.cleanup_movies()
             
         except Exception as e:
             logging.error('failed to cleanup media files: %(msg)s' % {
index cce949a99f4546d147f67bddc4fb599446e3df1e..46898be54dac05ff396684f287760f3a623d12db 100644 (file)
@@ -32,7 +32,7 @@ PORT = 8765
 # interval in seconds at which motionEye checks if motion is running
 MOTION_CHECK_INTERVAL = 10
 
-# interval in seconds at which the janitor is called to remove old images and movies
+# interval in seconds at which the janitor is called to remove old pictures and movies
 CLEANUP_INTERVAL = 43200
 
 # timeout in seconds to wait for responses when contacting a remote server
diff --git a/src/cleanup.py b/src/cleanup.py
deleted file mode 100644 (file)
index e63dad1..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-
-# Copyright (c) 2013 Calin Crisan
-# This file is part of motionEye.
-#
-# motionEye is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
-
-import datetime
-import logging
-import os
-
-import config
-
-
-def _remove_older_files(dir, moment, exts):
-    for root, dirs, files in os.walk(dir):  # @UnusedVariable
-        for name in files:
-            full_path = os.path.join(root, name)
-            if not os.path.isfile(full_path):
-                continue
-            
-            full_path_lower = full_path.lower()
-            if not [e for e in exts if full_path_lower.endswith(e)]:
-                continue
-            
-            file_moment = datetime.datetime.fromtimestamp(os.path.getmtime(full_path))
-            if file_moment < moment:
-                logging.debug('removing file %(path)s...' % {
-                        'path': full_path})
-                
-                os.remove(full_path)
-
-
-def cleanup_images():
-    logging.debug('cleaning up images...')
-    
-    for camera_id in config.get_camera_ids():
-        camera_config = config.get_camera(camera_id)
-        if camera_config.get('@proto') != 'v4l2':
-            continue
-        
-        preserve_images = camera_config.get('@preserve_images')
-        if preserve_images == 0:
-            return # preserve forever
-        
-        preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_images)
-            
-        target_dir = camera_config.get('target_dir')
-        _remove_older_files(target_dir, preserve_moment, exts=['.jpg', '.png'])
-
-
-def cleanup_movies():
-    logging.debug('cleaning up movies...')
-    
-    for camera_id in config.get_camera_ids():
-        camera_config = config.get_camera(camera_id)
-        if camera_config.get('@proto') != 'v4l2':
-            continue
-        
-        preserve_movies = camera_config.get('@preserve_movies')
-        if preserve_movies == 0:
-            return # preserve forever
-        
-        preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_movies)
-            
-        target_dir = camera_config.get('target_dir')
-        _remove_older_files(target_dir, preserve_moment, exts=['.avi'])
index 851e64ad5bbee8c2bde8a9c43bfa08eb27953ab5..657ba3a5d0b8bf45f8433961761dff94c2c109b7 100644 (file)
@@ -442,7 +442,7 @@ def camera_ui_to_dict(ui):
         'snapshot_interval': 0,
         'jpeg_filename': '',
         'snapshot_filename': '',
-        '@preserve_images': int(ui.get('preserve_images', 0)),
+        '@preserve_pictures': int(ui.get('preserve_pictures', 0)),
         
         # movies
         'ffmpeg_cap_new': ui.get('motion_movies', False),
@@ -616,7 +616,7 @@ def camera_dict_to_ui(data):
         'image_file_name': '%Y-%m-%d-%H-%M-%S',
         'image_quality': 85,
         'snapshot_interval': 0,
-        'preserve_images': data['@preserve_images'],
+        'preserve_pictures': data['@preserve_pictures'],
         
         # motion movies
         'motion_movies': data.get('ffmpeg_cap_new'),
@@ -983,7 +983,7 @@ def _set_default_motion_camera(data):
     data.setdefault('snapshot_interval', 0)
     data.setdefault('snapshot_filename', '')
     data.setdefault('quality', 85)
-    data.setdefault('@preserve_images', 0)
+    data.setdefault('@preserve_pictures', 0)
     
     data.setdefault('ffmpeg_variable_bitrate', 0)
     data.setdefault('ffmpeg_bps', 400000)
index 309ceae70a71cdeb61908c7a0d6a10b169fd5031..cb9d5fafaca62fcec698d1d92dafa213aee86dc9 100644 (file)
@@ -513,7 +513,7 @@ class ConfigHandler(BaseHandler):
         self.finish_json()
 
 
-class SnapshotHandler(BaseHandler):
+class PictureHandler(BaseHandler):
     @asynchronous
     def get(self, camera_id, op, filename=None):
         if camera_id is not None:
@@ -553,7 +553,7 @@ class SnapshotHandler(BaseHandler):
                 else:
                     self.finish(jpg)
             
-            remote.current_snapshot(
+            remote.current_picture(
                     camera_config['@host'],
                     camera_config['@port'],
                     camera_config['@username'],
@@ -562,15 +562,46 @@ class SnapshotHandler(BaseHandler):
                 
     @BaseHandler.auth()
     def list(self, camera_id):
-        logging.debug('listing snapshots for camera %(id)s' % {'id': camera_id})
+        logging.debug('listing pictures for camera %(id)s' % {'id': camera_id})
         
-        # TODO implement me
+        if camera_id not in config.get_camera_ids():
+            raise HTTPError(404, 'no such camera')
+        
+        camera_config = config.get_camera(camera_id)
+        if camera_config['@proto'] != 'v4l2':
+            def on_response(remote_list):
+                camera_url = remote.make_remote_camera_url(
+                        camera_config.get('@host'),
+                        camera_config.get('@port'),
+                        camera_config.get('@remote_camera_id'))
+                
+                camera_full_url = camera_config['@proto'] + '://' + camera_url
+                
+                if remote_list is None:
+                    return self.finish_json({'error': 'Failed to get picture list for %(url)s.' % {
+                            'url': camera_full_url}})
+
+                self.finish_json(remote_list)
+            
+            remote.list_pictures(
+                    camera_config.get('@host'),
+                    camera_config.get('@port'),
+                    camera_config.get('@username'),
+                    camera_config.get('@password'),
+                    camera_config.get('@remote_camera_id'), on_response)
+        
+        else:
+            pictures = []
+            
+            #for 
+                
+            self.finish_json({'pictures': pictures})
         
         self.finish_json()
     
     @BaseHandler.auth()
     def download(self, camera_id, filename):
-        logging.debug('downloading snapshot %(filename)s of camera %(id)s' % {
+        logging.debug('downloading picture %(filename)s of camera %(id)s' % {
                 'filename': filename, 'id': camera_id})
         
         # TODO implement me
diff --git a/src/mediafiles.py b/src/mediafiles.py
new file mode 100644 (file)
index 0000000..e423698
--- /dev/null
@@ -0,0 +1,81 @@
+
+# Copyright (c) 2013 Calin Crisan
+# This file is part of motionEye.
+#
+# motionEye is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
+
+import datetime
+import logging
+import os
+
+import config
+
+
+def _remove_older_files(dir, moment, exts):
+    for root, dirs, files in os.walk(dir):  # @UnusedVariable
+        for name in files:
+            full_path = os.path.join(root, name)
+            if not os.path.isfile(full_path):
+                continue
+            
+            full_path_lower = full_path.lower()
+            if not [e for e in exts if full_path_lower.endswith(e)]:
+                continue
+            
+            file_moment = datetime.datetime.fromtimestamp(os.path.getmtime(full_path))
+            if file_moment < moment:
+                logging.debug('removing file %(path)s...' % {
+                        'path': full_path})
+                
+                os.remove(full_path)
+
+
+def cleanup_pictures():
+    logging.debug('cleaning up pictures...')
+    
+    for camera_id in config.get_camera_ids():
+        camera_config = config.get_camera(camera_id)
+        if camera_config.get('@proto') != 'v4l2':
+            continue
+        
+        preserve_pictures = camera_config.get('@preserve_pictures')
+        if preserve_pictures == 0:
+            return # preserve forever
+        
+        preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_pictures)
+            
+        target_dir = camera_config.get('target_dir')
+        _remove_older_files(target_dir, preserve_moment, exts=['.jpg', '.png'])
+
+
+def cleanup_movies():
+    logging.debug('cleaning up movies...')
+    
+    for camera_id in config.get_camera_ids():
+        camera_config = config.get_camera(camera_id)
+        if camera_config.get('@proto') != 'v4l2':
+            continue
+        
+        preserve_movies = camera_config.get('@preserve_movies')
+        if preserve_movies == 0:
+            return # preserve forever
+        
+        preserve_moment = datetime.datetime.now() - datetime.timedelta(days=preserve_movies)
+            
+        target_dir = camera_config.get('target_dir')
+        _remove_older_files(target_dir, preserve_moment, exts=['.avi'])
+
+
+def list_pictures(config):
+    pass
\ No newline at end of file
index 216b4e8db3193469a98c41127cb71d51607ac515..d4afebfc0ab1f2754f5fdfd7f1e060acc3cc71d6 100644 (file)
@@ -173,15 +173,15 @@ def set_preview(host, port, username, password, camera_id, controls, callback):
     http_client.fetch(request, on_response)
 
 
-def current_snapshot(host, port, username, password, camera_id, callback):
+def current_picture(host, port, username, password, camera_id, callback):
     global _snapshot_cache
     
-    logging.debug('getting current snapshot for remote camera %(id)s on %(host)s:%(port)s' % {
+    logging.debug('getting current picture for remote camera %(id)s on %(host)s:%(port)s' % {
             'id': camera_id,
             'host': host,
             'port': port})
     
-    request = _make_request(host, port, username, password, '/snapshot/%(id)s/current/' % {'id': camera_id})
+    request = _make_request(host, port, username, password, '/picture/%(id)s/current/' % {'id': camera_id})
     
     cached = _snapshot_cache.setdefault(request.url, {'pending': 0, 'jpg': None})
     if cached['pending'] > 0: # a pending request for this snapshot exists
@@ -192,7 +192,7 @@ def current_snapshot(host, port, username, password, camera_id, callback):
         cached['jpg'] = response.body
         
         if response.error:
-            logging.error('failed to get current snapshot for remote camera %(id)s on %(host)s:%(port)s: %(msg)s' % {
+            logging.error('failed to get current picture for remote camera %(id)s on %(host)s:%(port)s: %(msg)s' % {
                     'id': camera_id,
                     'host': host,
                     'port': port,
@@ -206,3 +206,38 @@ def current_snapshot(host, port, username, password, camera_id, callback):
     
     http_client = AsyncHTTPClient()
     http_client.fetch(request, on_response)
+
+
+def list_pictures(host, port, username, password, camera_id, callback):
+    logging.debug('getting picture list for remote camera %(id)s on %(host)s:%(port)s' % {
+            'id': camera_id,
+            'host': host,
+            'port': port})
+    
+    request = _make_request(host, port, username, password, '/picture/%(id)s/list/' % {'id': camera_id})
+    
+    def on_response(response):
+        if response.error:
+            logging.error('failed to get picture list for remote camera %(id)s on %(host)s:%(port)s: %(msg)s' % {
+                    'id': camera_id,
+                    'host': host,
+                    'port': port,
+                    'msg': unicode(response.error)})
+            
+            return callback(None)
+        
+        try:
+            response = json.loads(response.body)
+            
+        except Exception as e:
+            logging.error('failed to decode json answer from %(host)s:%(port)s: %(msg)s' % {
+                    'host': host,
+                    'port': port,
+                    'msg': unicode(e)})
+            
+            return callback(None)
+        
+        return callback(response['pictures'])
+    
+    http_client = AsyncHTTPClient()
+    http_client.fetch(request, on_response)
index d35429c0a87225bc5b2acdcaf62ee6424a4366de..fcac5fa6e08f2c29ec75e1995644c834165577be 100644 (file)
@@ -44,8 +44,8 @@ application = Application(
         (r'^/config/main/(?P<op>set|get)/?$', handlers.ConfigHandler),
         (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'^/snapshot/(?P<camera_id>\d+)/(?P<op>current|list)/?$', handlers.SnapshotHandler),
-        (r'^/snapshot/(?P<camera_id>\d+)/(?P<op>download)/(?P<filename>.+)/?$', handlers.SnapshotHandler),
+        (r'^/picture/(?P<camera_id>\d+)/(?P<op>current|list)/?$', handlers.PictureHandler),
+        (r'^/picture/(?P<camera_id>\d+)/(?P<op>download)/(?P<filename>.+)/?$', handlers.PictureHandler),
         (r'^/movie/(?P<camera_id>\d+)/(?P<op>list)/?$', handlers.MovieHandler),
         (r'^/movie/(?P<camera_id>\d+)/(?P<op>download)/(?P<filename>.+)/?$', handlers.MovieHandler),
         (r'^/update/?$', handlers.UpdateHandler),
index e98b9037bca2715ad5eca7c7fa326b0ac1603271..234fc96efcee7ca3cf39b707702d28038d12eae4 100644 (file)
@@ -505,7 +505,7 @@ function cameraUi2Dict() {
         'image_quality': $('#imageQualitySlider').val(),
         'capture_mode': $('#captureModeSelect').val(),
         'snapshot_interval': $('#snapshotIntervalEntry').val(),
-        'preserve_images': $('#preserveImagesSelect').val(),
+        'preserve_pictures': $('#preservePicturesSelect').val(),
         
         /* motion movies */
         'motion_movies': $('#motionMoviesSwitch')[0].checked,
@@ -629,7 +629,7 @@ function dict2CameraUi(dict) {
     $('#imageQualitySlider').val(dict['image_quality']);
     $('#captureModeSelect').val(dict['capture_mode']);
     $('#snapshotIntervalEntry').val(dict['snapshot_interval']);
-    $('#preserveImagesSelect').val(dict['preserve_images']);
+    $('#preservePicturesSelect').val(dict['preserve_pictures']);
     
     /* motion movies */
     $('#motionMoviesSwitch')[0].checked = dict['motion_movies'];
@@ -1466,7 +1466,7 @@ function refreshCameraFrames() {
             timestamp /= 500;
         }
         timestamp = Math.round(timestamp);
-        img.src = '/snapshot/' + cameraId + '/current/?_=' + timestamp;
+        img.src = '/picture/' + cameraId + '/current/?_=' + timestamp;
     }
     
     var cameraFrames;
index 25a05a7907344dee2e1a58a914f954203f1c03e5..13dad7489b57773d9a50f7d1155af999a42cbb60 100644 (file)
                         <td><span class="help-mark" title="sets the interval (in seconds) for the automated snapshots">?</span></td>
                     </tr>
                     <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Preserve Images</span></td>
+                        <td class="settings-item-label"><span class="settings-item-label">Preserve Pictures</span></td>
                         <td class="settings-item-value">
-                            <select class="styled still-images" id="preserveImagesSelect">
+                            <select class="styled still-images" id="preservePicturesSelect">
                                 <option value="1">For One Day</option>
                                 <option value="7">For One Week</option>
                                 <option value="30">For One Month</option>