-> 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
def _start_cleanup():
- import cleanup
+ import mediafiles
def do_cleanup():
ioloop = tornado.ioloop.IOLoop.instance()
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' % {
# 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
+++ /dev/null
-
-# 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'])
'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),
'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'),
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)
self.finish_json()
-class SnapshotHandler(BaseHandler):
+class PictureHandler(BaseHandler):
@asynchronous
def get(self, camera_id, op, filename=None):
if camera_id is not None:
else:
self.finish(jpg)
- remote.current_snapshot(
+ remote.current_picture(
camera_config['@host'],
camera_config['@port'],
camera_config['@username'],
@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
--- /dev/null
+
+# 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
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
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,
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)
(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),
'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,
$('#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'];
timestamp /= 500;
}
timestamp = Math.round(timestamp);
- img.src = '/snapshot/' + cameraId + '/current/?_=' + timestamp;
+ img.src = '/picture/' + cameraId + '/current/?_=' + timestamp;
}
var cameraFrames;
<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>