import re
import shlex
import subprocess
+import urlparse
from tornado.ioloop import IOLoop
camera_ids = get_camera_ids()
cameras = [get_camera(camera_id) for camera_id in camera_ids]
- return bool([c for c in cameras if c.get('@enabled') and utils.local_camera(c)])
+ return bool([c for c in cameras if c.get('@enabled') and utils.local_motion_camera(c)])
def get_network_shares():
no_convert=['@name', '@network_share_name', '@network_server',
'@network_username', '@network_password', '@storage_device'])
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
# determine the enabled status
main_config = get_main()
threads = main_config.get('thread', [])
_camera_config_cache[camera_id] = camera_config
camera_config = dict(camera_config)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
old_motion = is_old_motion()
# adapt directives to old configuration, if needed
global _camera_ids_cache
global _camera_config_cache
+ proto = device_details['proto']
+ if proto in ['netcam', 'mjpeg']:
+ host = device_details['host']
+ if device_details['port']:
+ host += ':' + str(device_details['port'])
+
+ if device_details['username'] and proto == 'mjpeg':
+ if device_details['password']:
+ host = device_details['username'] + ':' + device_details['password'] + '@' + host
+
+ else:
+ host = device_details['username'] + '@' + host
+
+ device_details['url'] = urlparse.urlunparse((device_details['scheme'], host, device_details['uri'], '', '', ''))
+
# determine the last camera id
camera_ids = get_camera_ids()
# prepare a default camera config
camera_config = {'@enabled': True}
- if device_details['proto'] == 'v4l2':
+ if proto == 'v4l2':
+ # find a suitable resolution
+ for (w, h) in v4l2ctl.list_resolutions(device_details['uri']):
+ if w > 300:
+ camera_config['width'] = w
+ camera_config['height'] = h
+ break
+
+ camera_config['videodevice'] = device_details['uri']
_set_default_motion_camera(camera_id, camera_config)
- camera_config_ui = camera_dict_to_ui(camera_config)
- camera_config_ui.update(device_details)
- camera_config = camera_ui_to_dict(camera_config_ui)
- elif device_details['proto'] == 'motioneye':
+ elif proto == 'motioneye':
camera_config['@proto'] = 'motioneye'
+ camera_config['@scheme'] = device_details['scheme']
camera_config['@host'] = device_details['host']
camera_config['@port'] = device_details['port']
camera_config['@uri'] = device_details['uri']
camera_config['@password'] = device_details['password']
camera_config['@remote_camera_id'] = device_details['remote_camera_id']
- else: # assuming netcam
- camera_config['netcam_url'] = 'http://dummy' # used only to identify it as a netcam
+ elif proto == 'netcam':
+ camera_config['netcam_url'] = device_details['url']
+ if device_details['username']:
+ camera_config['netcam_userpass'] = device_details['username'] + ':' + device_details['password']
_set_default_motion_camera(camera_id, camera_config)
- camera_config_ui = camera_dict_to_ui(camera_config)
- camera_config_ui.update(device_details)
- camera_config = camera_ui_to_dict(camera_config_ui)
+
+ else: # assuming mjpeg
+ camera_config['@proto'] = 'mjpeg'
+ camera_config['@url'] = device_details['url']
# write the configuration to file
set_camera(camera_id, camera_config)
return ui
-def camera_ui_to_dict(ui):
+def camera_ui_to_dict(ui, old_config=None):
+ old_config = dict(old_config or {})
main_config = get_main() # needed for surveillance password
data = {
'on_event_end': ''
}
- if ui['proto'] == 'v4l2':
- # device
- data['videodevice'] = ui['uri']
+ if utils.v4l2_camera(old_config):
+ proto = 'v4l2'
+
+ else:
+ proto = 'netcam'
+
+ if proto == 'v4l2':
+ # leave videodevice unchanged
# resolution
if not ui['resolution']:
else:
data['hue'] = max(1, int(round(int(ui['hue']) * 2.55)))
- else: # assuming http/netcam
- # device
- data['netcam_url'] = ui['proto'] + '://' + ui['host']
- if ui['port']:
- data['netcam_url'] += ':' + str(ui['port'])
-
- data['netcam_url'] += ui['uri']
-
- if ui['username'] or ui['password']:
- data['netcam_userpass'] = (ui['username'] or '') + ':' + (ui['password'] or '')
-
+ else: # assuming netcam
+ # leave netcam_url unchanged
+ # leave netcam_userpass unchanged
data['netcam_keepalive'] = True
data['netcam_tolerant_check'] = True
else:
data['text_right'] = ui['custom_right_text']
- if ui['proto'] != 'v4l2' or data['width'] > 320:
+ if proto == 'netcam' or data['width'] > 320:
data['text_double'] = True
if ui['still_images']:
data['quality'] = max(1, int(ui['image_quality']))
if ui['motion_movies']:
- if ui['proto'] == 'v4l2':
+ if proto == 'v4l2':
max_val = data['width'] * data['height'] * data['framerate'] / 3
- else:
+ else: # always assume netcam image size of (640x480) - we have no means to test it
max_val = 640 * 480 * data['framerate'] / 3
max_val = min(max_val, 9999999)
for name, value in extra_options:
data[name] = value or ''
- return data
+ old_config.update(data)
+
+ return old_config
def camera_dict_to_ui(data):
}
if utils.net_camera(data):
- netcam_url = data.get('netcam_url')
- proto, rest = netcam_url.split('://')
- parts = rest.split('/', 1)
- if len(parts) > 1:
- host_port, uri = parts[:2]
- uri = '/' + uri
-
- else:
- host_port, uri = rest, ''
-
- parts = host_port.split(':')
- if len(parts) > 1:
- host, port = parts[:2]
-
- else:
- host, port = host_port, ''
+ ui['device_url'] = data['netcam_url']
+ ui['proto'] = 'netcam'
- ui['proto'] = proto
- ui['host'] = host
- ui['port'] = port
- ui['uri'] = uri
-
- userpass = data.get('netcam_userpass')
- if userpass:
- ui['username'], ui['password'] = userpass.split(':', 1)
-
- else:
- ui['username'], ui['password'] = '', ''
-
# width & height are not available for netcams,
# we have no other choice but use something like 640x480 as reference
threshold = data['threshold'] * 100.0 / (640 * 480)
else: # assuming v4l2
+ ui['device_url'] = data['videodevice']
ui['proto'] = 'v4l2'
- ui['host'], ui['port'] = None, None
- ui['uri'] = data['videodevice']
- ui['username'], ui['password'] = None, None
# resolutions
resolutions = v4l2ctl.list_resolutions(data['videodevice'])
# the brightness & co. keys in the ui dictionary
# indicate the presence of these controls
# we must call v4l2ctl functions to determine the available controls
- brightness = v4l2ctl.get_brightness(ui['uri'])
+ brightness = v4l2ctl.get_brightness(data['videodevice'])
if brightness is not None: # has brightness control
if data.get('brightness', 0) != 0:
ui['brightness'] = brightness
else:
ui['brightness'] = 50
- contrast = v4l2ctl.get_contrast(ui['uri'])
+ contrast = v4l2ctl.get_contrast(data['videodevice'])
if contrast is not None: # has contrast control
if data.get('contrast', 0) != 0:
ui['contrast'] = contrast
else:
ui['contrast'] = 50
- saturation = v4l2ctl.get_saturation(ui['uri'])
+ saturation = v4l2ctl.get_saturation(data['videodevice'])
if saturation is not None: # has saturation control
if data.get('saturation', 0) != 0:
ui['saturation'] = saturation
else:
ui['saturation'] = 50
- hue = v4l2ctl.get_hue(ui['uri'])
+ hue = v4l2ctl.get_hue(data['videodevice'])
if hue is not None: # has hue control
if data.get('hue', 0) != 0:
ui['hue'] = hue
if utils.v4l2_camera(data):
max_val = data['width'] * data['height'] * data['framerate'] / 3
- else:
+ else: # net camera
max_val = 640 * 480 * data['framerate'] / 3
max_val = min(max_val, 9999999)
raise HTTPError(404, 'no such camera')
local_config = config.get_camera(camera_id)
- if utils.local_camera(local_config):
+ if utils.local_motion_camera(local_config):
ui_config = config.camera_dict_to_ui(local_config)
self.finish_json(ui_config)
def on_response(remote_ui_config=None, error=None):
if error:
return self.finish_json({'error': 'Failed to get remote camera configuration for %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(local_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(local_config), 'msg': error}})
for key, value in local_config.items():
remote_ui_config[key.replace('@', '')] = value
+ # replace the real device URI with the remote camera URL
+ remote_ui_config['device_url'] = remote.pretty_camera_url(local_config)
self.finish_json(remote_ui_config)
remote.get_config(local_config, on_response)
raise HTTPError(404, 'no such camera')
local_config = config.get_camera(camera_id)
- if utils.local_camera(local_config):
- # certain parameters cannot be changed via ui_config;
- # we must preserve existing values for those params
- local_ui_config = config.camera_dict_to_ui(local_config)
- ui_config.setdefault('enabled', local_ui_config['enabled'])
- ui_config['proto'] = local_ui_config['proto']
- ui_config['host'] = local_ui_config['host']
- ui_config['port'] = local_ui_config['port']
- ui_config['uri'] = local_ui_config['uri']
- ui_config['username'] = local_ui_config['username']
- ui_config['password'] = local_ui_config['password']
-
- local_config = config.camera_ui_to_dict(ui_config)
+ if utils.local_motion_camera(local_config):
+ local_config = config.camera_ui_to_dict(ui_config, local_config)
+
config.set_camera(camera_id, local_config)
on_finish(None, True) # (no error, motion needs restart)
local_config['@enabled'] = ui_config['enabled']
config.set_camera(camera_id, local_config)
- # when the local_config supplied has only the enabled state,
- # the camera was probably disabled due to errors
-
if ui_config.has_key('name'):
def on_finish_wrapper(error=None):
return on_finish(error, False)
+ ui_config['enabled'] = True # never disable the camera remotely
remote.set_config(local_config, ui_config, on_finish_wrapper)
else:
+ # when the ui config supplied has only the enabled state
+ # and no useful fields (such as "name"),
+ # the camera was probably disabled due to errors
on_finish(None, False)
def set_main_config(ui_config):
if normal_credentials != old_normal_credentials:
logging.debug('surveillance credentials changed, all camera configs must be updated')
+ # reconfigure all local cameras to update the stream authentication options
for camera_id in config.get_camera_ids():
local_config = config.get_camera(camera_id)
- if not utils.local_camera(local_config):
+ if not utils.local_motion_camera(local_config):
continue
- # this will update the stream authentication options
ui_config = config.camera_dict_to_ui(local_config)
- local_config = config.camera_ui_to_dict(ui_config)
+ local_config = config.camera_ui_to_dict(ui_config, local_config)
+
config.set_camera(camera_id, local_config)
restart = True
remote.set_preview(camera_config, controls, on_response)
- else:
+ else: # not supported
self.finish_json({'error': True})
@BaseHandler.auth()
def list_cameras(self):
logging.debug('listing cameras')
- type = self.get_data().get('type')
- if type == 'motioneye': # remote listing
+ proto = self.get_data().get('proto')
+ if proto == 'motioneye': # remote listing
def on_response(cameras=None, error=None):
if error:
self.finish_json({'error': error})
remote.list_cameras(self.get_data(), on_response)
- elif type == 'netcam':
+ elif proto == 'netcam':
+ def on_response(cameras=None, error=None):
+ if error:
+ self.finish_json({'error': error})
+
+ else:
+ self.finish_json({'cameras': cameras})
+
+ utils.test_mjpeg_url(self.get_data(), auth_modes=['basic'], allow_jpeg=True, callback=on_response)
+
+ elif proto == 'mjpeg':
def on_response(cameras=None, error=None):
if error:
self.finish_json({'error': error})
else:
self.finish_json({'cameras': cameras})
- utils.test_netcam_url(self.get_data(), on_response)
+ utils.test_mjpeg_url(self.get_data(), auth_modes=['basic', 'digest'], allow_jpeg=False, callback=on_response)
- else: # assuming local listing
+ else: # assuming local motionEye camera listing
cameras = []
camera_ids = config.get_camera_ids()
if not config.get_main().get('@enabled'):
if error:
cameras.append({
'id': camera_id,
- 'name': '<' + remote.make_camera_url(local_config) + '>',
+ 'name': '<' + remote.pretty_camera_url(local_config) + '>',
'enabled': False,
'streaming_framerate': 1,
'framerate': 1
if local_config is None:
continue
- if utils.local_camera(local_config):
+ if utils.local_motion_camera(local_config):
ui_config = config.camera_dict_to_ui(local_config)
cameras.append(ui_config)
check_finished()
raise
- proto = device_details['proto']
- if proto == 'v4l2':
- # find a suitable resolution
- for (w, h) in v4l2ctl.list_resolutions(device_details['uri']):
- if w > 300:
- device_details.setdefault('resolution', str(w) + 'x' + str(h))
- break
-
- else:
- # adjust uri format
- if device_details['uri'] and not device_details['uri'].startswith('/'):
- device_details['uri'] = '/' + device_details['uri']
-
camera_id, camera_config = config.add_camera(device_details)
camera_config['@id'] = camera_id
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
motionctl.stop()
if settings.SMB_SHARES:
self.finish_json(remote_ui_config)
- remote.get_config(device_details, on_response)
+ remote.get_config(camera_config, on_response)
@BaseHandler.auth(admin=True)
def rem_camera(self, camera_id):
logging.debug('removing camera %(id)s' % {'id': camera_id})
- local = utils.local_camera(config.get_camera(camera_id))
+ local = utils.local_motion_camera(config.get_camera(camera_id))
config.rem_camera(camera_id)
if local:
logging.warn('ignoring event for remote camera with id %s (probably removed)' % camera_id)
return self.finish_json()
- if not utils.local_camera(camera_config):
+ if not utils.local_motion_camera(camera_config):
logging.warn('ignoring event for remote camera with id %s' % camera_id)
return self.finish_json()
return self.try_finish(picture)
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
picture = mediafiles.get_current_picture(camera_config,
width=width,
height=height)
logging.debug('listing pictures for camera %(id)s' % {'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
def on_media_list(media_list):
if media_list is None:
return self.finish_json({'error': 'Failed to get pictures list.'})
def on_response(remote_list=None, error=None):
if error:
return self.finish_json({'error': 'Failed to get picture list for %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json(remote_list)
def frame(self, camera_id):
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config) or self.get_argument('title', None) is not None:
+ if utils.local_motion_camera(camera_config) or self.get_argument('title', None) is not None:
self.render('main.html',
frame=True,
camera_id=camera_id,
camera_config=camera_config,
title=self.get_argument('title', ''))
+ # issue a fake camera_ui_to_dict() call to transform
+ # the remote UI values into motion config directives
remote_config = config.camera_ui_to_dict(remote_ui_config)
+
self.render('main.html',
frame=True,
camera_id=camera_id,
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
content = mediafiles.get_media_content(camera_config, filename, 'picture')
pretty_filename = camera_config['@name'] + '_' + os.path.basename(filename)
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to download picture from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
pretty_filename = os.path.basename(filename) # no camera name available w/o additional request
self.set_header('Content-Type', 'image/jpeg')
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
content = mediafiles.get_media_preview(camera_config, filename, 'picture',
width=self.get_argument('width', None),
height=self.get_argument('height', None))
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
try:
mediafiles.del_media_content(camera_config, filename, 'picture')
self.finish_json()
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}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json()
logging.debug('serving zip file for group %(group)s of camera %(id)s with key %(key)s' % {
'group': group, 'id': camera_id, 'key': key})
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
data = mediafiles.get_prepared_cache(key)
if not data:
logging.error('prepared cache data for key "%s" does not exist' % key)
raise HTTPError(404, 'no such key')
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
pretty_filename = camera_config['@name'] + '_' + group
pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename)
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to download zip file from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.set_header('Content-Type', response['content_type'])
self.set_header('Content-Disposition', response['content_disposition'])
logging.debug('preparing zip file for group %(group)s of camera %(id)s' % {
'group': group, 'id': camera_id})
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
def on_zip(data):
if data is None:
return self.finish_json({'error': 'Failed to create zip file.'})
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to make zip file at %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json({'key': response['key']})
logging.debug('serving timelapse movie for group %(group)s of camera %(id)s with key %(key)s' % {
'group': group, 'id': camera_id, 'key': key})
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
data = mediafiles.get_prepared_cache(key)
if data is None:
logging.error('prepared cache data for key "%s" does not exist' % key)
raise HTTPError(404, 'no such key')
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
pretty_filename = camera_config['@name'] + '_' + group
pretty_filename = re.sub('[^a-zA-Z0-9]', '_', pretty_filename)
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to download timelapse movie from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.set_header('Content-Type', response['content_type'])
self.set_header('Content-Disposition', response['content_disposition'])
logging.debug('checking timelapse movie status for group %(group)s of camera %(id)s' % {
'group': group, 'id': camera_id})
- if utils.local_camera(camera_config):
+ 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'])
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to check timelapse movie progress at %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
if response['progress'] == -1 and response.get('key'):
self.finish_json({'key': response['key'], 'progress': -1})
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})
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
status = mediafiles.check_timelapse_movie()
if status['progress'] != -1:
self.finish_json({'progress': status['progress']}) # timelapse already active
def on_status(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
if response['progress'] != -1:
return self.finish_json({'progress': response['progress']}) # timelapse already active
def on_make(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to make timelapse movie at %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json({'progress': -1})
'group': group, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
try:
mediafiles.del_media_group(camera_config, group, 'picture')
self.finish_json()
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to delete picture group from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json()
logging.debug('listing movies for camera %(id)s' % {'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
def on_media_list(media_list):
if media_list is None:
return self.finish_json({'error': 'Failed to get movies list.'})
def on_response(remote_list=None, error=None):
if error:
return self.finish_json({'error': 'Failed to get movie list for %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json(remote_list)
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
content = mediafiles.get_media_content(camera_config, filename, 'movie')
pretty_filename = camera_config['@name'] + '_' + os.path.basename(filename)
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to download movie from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
pretty_filename = os.path.basename(filename) # no camera name available w/o additional request
self.set_header('Content-Type', 'video/mpeg')
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
content = mediafiles.get_media_preview(camera_config, filename, 'movie',
width=self.get_argument('width', None),
height=self.get_argument('height', None))
'filename': filename, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
try:
mediafiles.del_media_content(camera_config, filename, 'movie')
self.finish_json()
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}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json()
'group': group, 'id': camera_id})
camera_config = config.get_camera(camera_id)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
try:
mediafiles.del_media_group(camera_config, group, 'movie')
self.finish_json()
def on_response(response=None, error=None):
if error:
return self.finish_json({'error': 'Failed to delete movie group from %(url)s: %(msg)s.' % {
- 'url': remote.make_camera_url(camera_config), 'msg': error}})
+ 'url': remote.pretty_camera_url(camera_config), 'msg': error}})
self.finish_json()
for camera_id in config.get_camera_ids():
camera_config = config.get_camera(camera_id)
- if not utils.local_camera(camera_config):
+ if not utils.local_motion_camera(camera_config):
continue
preserve_media = camera_config.get('@preserve_%(media_type)ss' % {'media_type': media_type}, 0)
count = 0
for camera_id in config.get_camera_ids():
camera_config = config.get_camera(camera_id)
- if not utils.local_camera(camera_config):
+ if not utils.local_motion_camera(camera_config):
continue
target_dir = camera_config['target_dir']
'camera_id': camera_id})
camera_config = config.get_camera(camera_id)
- if not camera_config['@enabled'] or not utils.local_camera(camera_config):
+ if not camera_config['@enabled'] or not utils.local_motion_camera(camera_config):
logging.error('could not start mjpg client for camera id %(camera_id)s: not enabled or not local' % {
'camera_id': camera_id})
def _disable_initial_motion_detection():
for camera_id in config.get_camera_ids():
camera_config = config.get_camera(camera_id)
- if not utils.local_camera(camera_config):
+ if not utils.local_motion_camera(camera_config):
continue
if not camera_config['@motion_detection']:
logging.error('failed to %(what)s motion detection for camera with id %(id)s: %(msg)s' % {
'what': ['disable', 'enable'][enabled],
'id': camera_id,
- 'msg': utils.pretty_http_error(response.error)})
+ 'msg': utils.pretty_http_error(response)})
else:
logging.debug('successfully %(what)s motion detection for camera with id %(id)s' % {
thread_id = 0
for cid in camera_ids:
camera_config = config.get_camera(cid)
- if utils.local_camera(camera_config):
+ if utils.local_motion_camera(camera_config):
thread_id += 1
if cid == camera_id:
_DOUBLE_SLASH_REGEX = re.compile('//+')
-def _make_request(host, port, username, password, uri, method='GET', data=None, query=None, timeout=None):
+def _make_request(scheme, host, port, username, password, uri, method='GET', data=None, query=None, timeout=None):
uri = _DOUBLE_SLASH_REGEX.sub('/', uri)
url = '%(scheme)s://%(host)s%(port)s%(uri)s' % {
- 'scheme': 'http',
+ 'scheme': scheme,
'host': host,
'port': ':' + str(port) if port else '',
'uri': uri or ''}
return wrapper
-def make_camera_url(local_config, camera=True):
+def pretty_camera_url(local_config, camera=True):
+ scheme = local_config.get('@scheme', local_config.get('scheme')) or 'http'
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')) or ''
uri = local_config.get('@uri', local_config.get('uri')) or ''
- url = 'motioneye://' + username + '@' + host
- if port:
+ url = scheme + '://' + host
+ if port and str(port) not in ['80', '443']:
url += ':' + str(port)
if uri:
url += uri
-
+
+ if url.endswith('/'):
+ url = url[:-1]
+
if camera:
if camera is True:
url += '/config/' + str(local_config.get('@remote_camera_id', local_config.get('remote_camera_id')))
return url
+def _remote_params(local_config):
+ return (
+ local_config.get('@scheme', local_config.get('scheme')) or 'http',
+ local_config.get('@host', local_config.get('host')),
+ local_config.get('@port', local_config.get('port')),
+ local_config.get('@username', local_config.get('username')),
+ local_config.get('@password', local_config.get('password')),
+ local_config.get('@uri', local_config.get('uri')) or '',
+ local_config.get('@remote_camera_id', local_config.get('remote_camera_id')))
+
+
def list_cameras(local_config, callback):
- 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 ''
+ scheme, host, port, username, password, uri, _ = _remote_params(local_config)
logging.debug('listing remote cameras on %(url)s' % {
- 'url': make_camera_url(local_config, camera=False)})
+ 'url': pretty_camera_url(local_config, camera=False)})
- request = _make_request(host, port, username, password, uri + '/config/list/')
+ request = _make_request(scheme, host, port, username, password, uri + '/config/list/')
def on_response(response):
if response.error:
logging.error('failed to list remote cameras on %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config, camera=False),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config, camera=False),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
response = json.loads(response.body)
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config, camera=False),
+ 'url': pretty_camera_url(local_config, camera=False),
'msg': unicode(e)})
return callback(error=unicode(e))
- callback(response['cameras'])
+ cameras = response['cameras']
+
+ # filter out simple mjpeg cameras
+ cameras = [c for c in cameras if c['proto'] != 'mjpeg']
+
+ callback(cameras)
http_client = AsyncHTTPClient()
http_client.fetch(request, _callback_wrapper(on_response))
def get_config(local_config, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('getting config for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
- request = _make_request(host, port, username, password, uri + '/config/%(id)s/get/' % {'id': camera_id})
+ request = _make_request(scheme, host, port, username, password, uri + '/config/%(id)s/get/' % {'id': camera_id})
def on_response(response):
if response.error:
logging.error('failed to get config for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
response = json.loads(response.body)
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'msg': unicode(e)})
return callback(error=unicode(e))
def set_config(local_config, ui_config, callback):
- host = local_config.get('@host')
- port = local_config.get('@port')
- username = local_config.get('@username')
- password = local_config.get('@password')
- uri = local_config.get('@uri') or ''
- camera_id = local_config.get('@remote_camera_id')
-
- # make sure these values never get to the remote instance
- local_config.pop('enabled', None)
- local_config.pop('proto', None)
- local_config.pop('host', None)
- local_config.pop('port', None)
- local_config.pop('uri', None)
- local_config.pop('username', None)
- local_config.pop('password', None)
-
+ scheme = local_config.get('@scheme', local_config.get('scheme'))
+ 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('setting config for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
ui_config = json.dumps(ui_config)
- request = _make_request(host, port, username, password, uri + '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=ui_config)
+ request = _make_request(scheme, host, port, username, password, uri + '/config/%(id)s/set/' % {'id': camera_id}, method='POST', data=ui_config)
def on_response(response):
if response.error:
logging.error('failed to set config for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback()
def set_preview(local_config, controls, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('setting preview for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
data = json.dumps(controls)
- request = _make_request(host, port, username, password, uri + '/config/%(id)s/set_preview/' % {'id': camera_id}, method='POST', data=data)
+ request = _make_request(scheme, host, port, username, password, uri + '/config/%(id)s/set_preview/' % {'id': camera_id}, method='POST', data=data)
def on_response(response):
if response.error:
logging.error('failed to set preview for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback()
def get_current_picture(local_config, width, height, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('getting current picture for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
query = {}
if height:
query['height'] = str(height)
- request = _make_request(host, port, username, password, uri + '/picture/%(id)s/current/' % {'id': camera_id}, query=query)
+ request = _make_request(scheme, host, port, username, password, uri + '/picture/%(id)s/current/' % {'id': camera_id}, query=query)
def on_response(response):
motion_detected = False
if response.error:
logging.error('failed to get current picture for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback(motion_detected, response.body)
def list_media(local_config, media_type, prefix, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('getting media list for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
query = {}
if prefix is not None:
query['prefix'] = prefix
# timeout here is 10 times larger than usual - we expect a big delay when fetching the media list
- request = _make_request(host, port, username, password, uri + '/%(media_type)s/%(id)s/list/' % {
+ request = _make_request(scheme, host, port, username, password, uri + '/%(media_type)s/%(id)s/list/' % {
'id': camera_id, 'media_type': media_type}, query=query, timeout=10 * settings.REMOTE_REQUEST_TIMEOUT)
def on_response(response):
if response.error:
logging.error('failed to get media list for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
response = json.loads(response.body)
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'msg': unicode(e)})
return callback(error=unicode(e))
def get_media_content(local_config, filename, media_type, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('downloading file %(filename)s of remote camera %(id)s on %(url)s' % {
'filename': filename,
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
uri += '/%(media_type)s/%(id)s/download/%(filename)s' % {
'media_type': media_type,
'filename': filename}
# timeout here is 10 times larger than usual - we expect a big delay when fetching the media list
- request = _make_request(host, port, username, password, uri, timeout=10 * settings.REMOTE_REQUEST_TIMEOUT)
+ request = _make_request(scheme, host, port, username, password, uri, timeout=10 * settings.REMOTE_REQUEST_TIMEOUT)
def on_response(response):
if response.error:
logging.error('failed to download 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': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
return callback(response.body)
def make_zipped_content(local_config, media_type, group, callback):
- 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'))
+ scheme, host, port, username, password, uri, 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,
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
prepare_uri = uri + '/%(media_type)s/%(id)s/zipped/%(group)s/' % {
'media_type': media_type,
'group': group}
# timeout here is 100 times larger than usual - we expect a big delay
- request = _make_request(host, port, username, password, prepare_uri, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT)
+ request = _make_request(scheme, host, port, username, password, prepare_uri, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT)
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,
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
key = json.loads(response.body)['key']
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'msg': unicode(e)})
return callback(error=unicode(e))
def get_zipped_content(local_config, media_type, key, group, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('downloading zip file for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
- request = _make_request(host, port, username, password, uri + '/%(media_type)s/%(id)s/zipped/%(group)s/?key=%(key)s' % {
+ request = _make_request(scheme, host, port, username, password, uri + '/%(media_type)s/%(id)s/zipped/%(group)s/?key=%(key)s' % {
'media_type': media_type,
'group': group,
'id': camera_id,
if response.error:
logging.error('failed to download zip file for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback({
'data': response.body,
def make_timelapse_movie(local_config, framerate, interval, group, callback):
- 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'))
+ scheme, host, port, username, password, uri, 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,
'id': camera_id,
'framerate': framerate,
'int': interval,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
uri += '/picture/%(id)s/timelapse/%(group)s/?interval=%(int)s&framerate=%(framerate)s' % {
'id': camera_id,
'framerate': framerate,
'group': group}
- request = _make_request(host, port, username, password, uri, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT)
+ request = _make_request(scheme, host, port, username, password, uri, timeout=100 * settings.REMOTE_REQUEST_TIMEOUT)
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,
'id': camera_id,
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'int': interval,
'framerate': framerate,
- 'msg': utils.pretty_http_error(response.error)})
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
response = json.loads(response.body)
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'msg': unicode(e)})
return callback(error=unicode(e))
def check_timelapse_movie(local_config, group, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('checking timelapse movie status for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
- request = _make_request(host, port, username, password, uri + '/picture/%(id)s/timelapse/%(group)s/?check=true' % {
+ request = _make_request(scheme, host, port, username, password, uri + '/picture/%(id)s/timelapse/%(group)s/?check=true' % {
'id': camera_id,
'group': group})
if response.error:
logging.error('failed to check timelapse movie status for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
try:
response = json.loads(response.body)
except Exception as e:
logging.error('failed to decode json answer from %(url)s: %(msg)s' % {
- 'url': make_camera_url(local_config),
+ 'url': pretty_camera_url(local_config),
'msg': unicode(e)})
return callback(error=unicode(e))
def get_timelapse_movie(local_config, key, group, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('downloading timelapse movie for remote camera %(id)s on %(url)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
- request = _make_request(host, port, username, password, uri + '/picture/%(id)s/timelapse/%(group)s/?key=%(key)s' % {
+ request = _make_request(scheme, host, port, username, password, uri + '/picture/%(id)s/timelapse/%(group)s/?key=%(key)s' % {
'id': camera_id,
'group': group,
'key': key},
if response.error:
logging.error('failed to download timelapse movie for remote camera %(id)s on %(url)s: %(msg)s' % {
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback({
'data': response.body,
def get_media_preview(local_config, filename, media_type, width, height, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('getting file preview for %(filename)s of remote camera %(id)s on %(url)s' % {
'filename': filename,
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
uri += '/%(media_type)s/%(id)s/preview/%(filename)s' % {
'media_type': media_type,
if height:
query['height'] = str(height)
- request = _make_request(host, port, username, password, uri, query=query)
+ request = _make_request(scheme, host, port, username, password, uri, query=query)
def on_response(response):
if response.error:
logging.error('failed to get file preview for %(filename)s of remote camera %(id)s on %(url)s: %(msg)s' % {
'filename': filename,
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback(response.body)
def del_media_content(local_config, filename, media_type, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
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)})
+ 'url': pretty_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', data='{}', timeout=settings.REMOTE_REQUEST_TIMEOUT)
+ request = _make_request(scheme, host, port, username, password, uri, method='POST', data='{}', 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': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback()
def del_media_group(local_config, group, media_type, callback):
- 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'))
+ scheme, host, port, username, password, uri, camera_id = _remote_params(local_config)
logging.debug('deleting group %(group)s of remote camera %(id)s on %(url)s' % {
'group': group,
'id': camera_id,
- 'url': make_camera_url(local_config)})
+ 'url': pretty_camera_url(local_config)})
uri += '/%(media_type)s/%(id)s/delete_all/%(group)s/' % {
'media_type': media_type,
'id': camera_id,
'group': group}
- request = _make_request(host, port, username, password, uri, method='POST', data='{}', timeout=settings.REMOTE_REQUEST_TIMEOUT)
+ request = _make_request(scheme, host, port, username, password, uri, method='POST', data='{}', timeout=settings.REMOTE_REQUEST_TIMEOUT)
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,
'id': camera_id,
- 'url': make_camera_url(local_config),
- 'msg': utils.pretty_http_error(response.error)})
+ 'url': pretty_camera_url(local_config),
+ 'msg': utils.pretty_http_error(response)})
- return callback(error=utils.pretty_http_error(response.error))
+ return callback(error=utils.pretty_http_error(response))
callback()
return '%.1f %s' % (size, unit)
-def pretty_http_error(http_error):
- msg = unicode(http_error)
+def pretty_http_error(response):
+ if response.code == 401 or response.error == 'Authentication Error':
+ return 'authentication failed'
+
+ if not response.error:
+ return 'ok'
+
+ msg = unicode(response.error)
if msg.startswith('HTTP '):
msg = msg.split(':', 1)[-1].strip()
if msg.startswith('[Errno '):
msg = msg.split(']', 1)[-1].strip()
+
+ if 'timeout' in msg.lower() or 'timed out' in msg.lower():
+ msg = 'request timed out'
return msg
return (used_size, total_size)
-def local_camera(config):
+def local_motion_camera(config):
+ '''Tells if a camera is managed by the local motion instance.'''
return bool(config.get('videodevice') or config.get('netcam_url'))
def remote_camera(config):
+ '''Tells if a camera is managed by a remote motionEye server.'''
return config.get('@proto') == 'motioneye'
def v4l2_camera(config):
+ '''Tells if a camera is a v4l2 device managed by the local motion instance.'''
return bool(config.get('videodevice'))
def net_camera(config):
+ '''Tells if a camera is a network camera managed by the local motion instance.'''
return bool(config.get('netcam_url'))
-def test_netcam_url(data, callback):
+def simple_mjpeg_camera(config):
+ '''Tells if a camera is a simple MJPEG camera not managed by any motion instance.'''
+ return bool(config.get('@proto') == 'mjpeg')
+
+
+def test_mjpeg_url(data, auth_modes, allow_jpeg, callback):
data = dict(data)
- data.setdefault('proto', 'http')
+ data.setdefault('scheme', 'http')
data.setdefault('host', '127.0.0.1')
data.setdefault('port', '80')
data.setdefault('uri', '')
data.setdefault('username', None)
data.setdefault('password', None)
- url = '%(proto)s://%(host)s%(port)s%(uri)s' % {
- 'proto': data['proto'],
+ url = '%(scheme)s://%(host)s%(port)s%(uri)s' % {
+ 'scheme': data['scheme'],
'host': data['host'],
'port': ':' + str(data['port']) if data['port'] else '',
'uri': data['uri'] or ''}
called = [False]
status_2xx = [False]
- auth_modes = ['basic'] # 'digest' netcams are not supported by motion yet
def on_header(header):
header = header.lower()
content_type = header.split(':')[1].strip()
called[0] = True
- if content_type in ['image/jpg', 'image/jpeg', 'image/pjpg']:
+ if content_type in ['image/jpg', 'image/jpeg', 'image/pjpg'] and allow_jpeg:
callback([{'id': 1, 'name': 'JPEG Network Camera'}])
elif content_type.startswith('multipart/x-mixed-replace'):
callback([{'id': 1, 'name': 'MJPEG Network Camera'}])
else:
- callback(error='not a network camera')
+ callback(error='not a supported network camera')
else:
# check for the status header
else:
called[0] = True
- callback(error=pretty_http_error(response.error) if response.error else 'not a network camera')
+ callback(error=pretty_http_error(response) if response.error else 'not a supported network camera')
username = data['username'] or None
password = data['password'] or None
now = datetime.datetime.now()
for camera_id in config.get_camera_ids():
camera_config = config.get_camera(camera_id)
- if not utils.local_camera(camera_config):
+ if not utils.local_motion_camera(camera_config):
continue
working_schedule = camera_config.get('@working_schedule')
font-size: 0.7em;
}
+div#addCameraInfo {
+ font-size: 0.7em;
+ max-width: 33em;
+}
div.media-dialog {
}
width: 3.5em;
}
+input[disabled] {
+ border: 1px solid #555 !important;
+}
/* combo box */
font-size: 0.9em;
}
+div.dialog-item-separator {
+ height: 1px;
+ border-top: 1px solid #414141;
+ margin: 0.5em 1em;
+}
+
/* popup message */
return s.toString();
};
-function makeDeviceUrl(dict) {
- switch (dict.proto) {
- case 'v4l2':
- return dict.proto + '://' + dict.uri;
-
- case 'motioneye':
- return dict.proto + '://' + dict.host + (dict.port ? ':' + dict.port : '') + dict.uri + '/config/' + dict.remote_camera_id;
-
- default: /* assuming netcam */
- return dict.proto + '://' + dict.host + (dict.port ? ':' + dict.port : '') + dict.uri;
- }
-}
-
function getCookie(name) {
if (document.cookie.length <= 0) {
return null;
'auto_brightness': $('#autoBrightnessSwitch')[0].checked,
'rotation': $('#rotationSelect').val(),
'framerate': $('#framerateSlider').val(),
- 'proto': $('#deviceEntry')[0].proto,
- 'host': $('#deviceEntry')[0].host,
- 'port': $('#deviceEntry')[0].port,
- 'uri': $('#deviceEntry')[0].uri,
- 'username': $('#deviceEntry')[0].username,
- 'password': $('#deviceEntry')[0].password,
'extra_options': $('#extraOptionsEntry').html().split(new RegExp('(<br[^>]*>)|(<div>)|(<p>)')).map(function (o) {
if (!o) {
return null;
}
/* video device */
+ var prettyType = '';
+ switch (dict['proto']) {
+ case 'v4l2':
+ prettyType = 'V4L2 Camera';
+ break;
+
+ case 'netcam':
+ prettyType = 'Network Camera';
+ break;
+
+ case 'motioneye':
+ prettyType = 'Remote motionEye Camera';
+ break;
+
+ case 'mjpeg':
+ prettyType = 'Simple MJPEG Camera';
+ break;
+ }
+
$('#videoDeviceSwitch')[0].checked = dict['enabled'];
$('#deviceNameEntry').val(dict['name']);
- $('#deviceEntry').val(makeDeviceUrl(dict));
- $('#deviceEntry')[0].proto = dict['proto'];
- $('#deviceEntry')[0].host = dict['host'];
- $('#deviceEntry')[0].port = dict['port'];
- $('#deviceEntry')[0].uri= dict['uri'];
- $('#deviceEntry')[0].username = dict['username'];
- $('#deviceEntry')[0].password = dict['password'];
+ $('#deviceUriEntry').val(dict['device_url']);
+ $('#deviceTypeEntry').val(prettyType);
$('#lightSwitchDetectSwitch')[0].checked = dict['light_switch_detect'];
$('#autoBrightnessSwitch')[0].checked = dict['auto_brightness'];
var mjpgUrl = location.protocol + '//' + location.host.split(':')[0] + ':' + dict.streaming_port;
var embedUrl = cameraUrl + 'frame/';
- if (dict.proto == 'motioneye') {
+ if (dict.proto == 'motioneye') { // TODO what about other protocols
/* cannot tell the mjpg streaming url for a remote motionEye camera */
mjpgUrl = '';
}
}
var instance;
- if (config.proto == 'http' || config.proto == 'v4l2') {
+ if (config.proto == 'netcam' || config.proto == 'v4l2') {
instance = '';
}
else { /* motioneye */
var cameraIdsByInstance = {};
$('div.camera-frame').each(function () {
var instance;
- if (this.config.proto == 'http' || this.config.proto == 'v4l2') {
+ if (this.config.proto == 'netcam' || this.config.proto == 'v4l2') {
instance = '';
}
else { /* motioneye */
'<tr>' +
'<td class="dialog-item-label"><span class="dialog-item-label">Device</span></td>' +
'<td class="dialog-item-value"><select class="styled" id="deviceSelect"></select></td>' +
- '<td><span class="help-mark" title="the device you wish to add to motionEye">?</span></td>' +
+ '<td><span class="help-mark" title="the device type you wish to add">?</span></td>' +
'</tr>' +
- '<tr class="motioneye netcam">' +
+ '<tr class="motioneye netcam mjpeg">' +
'<td class="dialog-item-label"><span class="dialog-item-label">URL</span></td>' +
'<td class="dialog-item-value"><input type="text" class="styled" id="urlEntry" placeholder="http://example.com:8080/cams/..."></td>' +
- '<td><span class="help-mark" title="the camera URL (e.g. http://example.com:8080/cams/)">?</span></td>' +
+ '<td><span class="help-mark" title="the camera URL (e.g. http://example.com:8080/cam/)">?</span></td>' +
'</tr>' +
- '<tr class="motioneye netcam">' +
+ '<tr class="motioneye netcam mjpeg">' +
'<td class="dialog-item-label"><span class="dialog-item-label">Username</span></td>' +
'<td class="dialog-item-value"><input type="text" class="styled" id="usernameEntry" placeholder="username..."></td>' +
'<td><span class="help-mark" title="the username for the URL, if required (e.g. admin)">?</span></td>' +
'</tr>' +
- '<tr class="motioneye netcam">' +
+ '<tr class="motioneye netcam mjpeg">' +
'<td class="dialog-item-label"><span class="dialog-item-label">Password</span></td>' +
'<td class="dialog-item-value"><input type="password" class="styled" id="passwordEntry" placeholder="password..."></td>' +
'<td><span class="help-mark" title="the password for the URL, if required">?</span></td>' +
'</tr>' +
- '<tr class="motioneye netcam">' +
+ '<tr class="motioneye netcam mjpeg">' +
'<td class="dialog-item-label"><span class="dialog-item-label">Camera</span></td>' +
'<td class="dialog-item-value"><select class="styled" id="addCameraSelect"></select><span id="cameraMsgLabel"></span></td>' +
- '<td><span class="help-mark" title="the camera you wish to add to motionEye">?</span></td>' +
+ '<td><span class="help-mark" title="the camera you wish to add">?</span></td>' +
+ '</tr>' +
+ '<tr class="motioneye netcam mjpeg">' +
+ '<td colspan="100"><div class="dialog-item-separator"></div></td>' +
+ '</tr>' +
+ '<tr class="motioneye netcam mjpeg">' +
+ '<td class="dialog-item-value" colspan="100"><div id="addCameraInfo">blah blah sdfsdf ana are mere multe verzi si vesele si mari</div></td>' +
'</tr>' +
'</table>');
var usernameEntry = content.find('#usernameEntry');
var passwordEntry = content.find('#passwordEntry');
var addCameraSelect = content.find('#addCameraSelect');
+ var addCameraInfo = content.find('#addCameraInfo');
var cameraMsgLabel = content.find('#cameraMsgLabel');
/* make validators */
makeComboValidator(addCameraSelect, true);
/* ui interaction */
- content.find('tr.motioneye, tr.netcam').css('display', 'none');
-
function updateUi() {
- content.find('tr.motioneye, tr.netcam').css('display', 'none');
+ content.find('tr.motioneye, tr.netcam, tr.mjpeg').css('display', 'none');
if (deviceSelect.val() == 'motioneye') {
content.find('tr.motioneye').css('display', 'table-row');
addCameraSelect.hide();
usernameEntry.val('admin');
usernameEntry.attr('readonly', 'readonly');
+ addCameraInfo.html(
+ 'Remote motionEye cameras are cameras installed behind another motionEye server. ' +
+ 'Adding them here will allow you to view and manage them remotely.');
}
else if (deviceSelect.val() == 'netcam') {
usernameEntry.removeAttr('readonly');
content.find('tr.netcam').css('display', 'table-row');
addCameraSelect.hide();
+ addCameraInfo.html(
+ 'Network cameras (or IP cameras) are devices that natively stream MJPEG videos or plain JPEG images. ' +
+ "Consult your device's manual to find out the correct MJPEG (or JPEG) URL.");
+ }
+ else if (deviceSelect.val() == 'mjpeg') {
+ usernameEntry.removeAttr('readonly');
+
+ /* make sure there is one trailing slash so that
+ * an URI can be detected */
+ var url = urlEntry.val().trim();
+ var m = url.match(new RegExp('/', 'g'));
+ if (m && m.length < 3 && !url.endsWith('/')) {
+ urlEntry.val(url + '/');
+ }
+
+ content.find('tr.mjpeg').css('display', 'table-row');
+ addCameraSelect.hide();
+ addCameraInfo.html(
+ 'Adding your device as a simple MJPEG camera instead of as a network camera will improve the framerate, ' +
+ 'but no motion detection or other advanced features will be available for it. ' +
+ 'The camera must be accessible to both your server and your browser. ' +
+ 'This type of camera is not compatible with Internet Explorer.');
}
updateModalDialogPosition();
this.validate();
});
- if (content.is(':visible') && uiValid() && (deviceSelect.val() == 'motioneye' || deviceSelect.val() == 'netcam')) {
+ if (content.is(':visible') && uiValid() && (deviceSelect.val() in {'motioneye': 1, 'netcam': 1, 'mjpeg': 1})) {
fetchRemoteCameras();
}
}
function splitCameraUrl(url) {
var parts = url.split('://');
- var proto = parts[0];
+ var scheme = parts[0];
var index = parts[1].indexOf('/');
var host = null;
var uri = '';
}
return {
- proto: proto,
+ scheme: scheme,
host: host,
port: port,
uri: uri
var data = splitCameraUrl(urlEntry.val());
data.username = usernameEntry.val();
data.password = passwordEntry.val();
- data.type = deviceSelect.val();
+ data.proto = deviceSelect.val();
cameraMsgLabel.html('');
deviceSelect.append('<option value="netcam">Network camera...</option>');
deviceSelect.append('<option value="motioneye">Remote motionEye camera...</option>');
+ //deviceSelect.append('<option value="mjpeg">Simple MJPEG camera...</option>');
updateUi();
data = splitCameraUrl(urlEntry.val());
data.username = usernameEntry.val();
data.password = passwordEntry.val();
+ data.proto = 'netcam';
+ }
+ else if (deviceSelect.val() == 'mjpeg') {
+ data = splitCameraUrl(urlEntry.val());
+ data.username = usernameEntry.val();
+ data.password = passwordEntry.val();
+ data.proto = 'mjpeg';
}
else { /* assuming v4l2 */
data.proto = 'v4l2';
</tr>
<tr class="settings-item advanced-setting">
<td class="settings-item-label"><span class="settings-item-label">Camera Device</span></td>
- <td class="settings-item-value"><input type="text" class="styled device camera-config" id="deviceEntry" disabled="disabled"></td>
+ <td class="settings-item-value"><input type="text" class="styled device camera-config" id="deviceUriEntry" disabled="disabled"></td>
+ </tr>
+ <tr class="settings-item advanced-setting">
+ <td class="settings-item-label"><span class="settings-item-label">Camera Type</span></td>
+ <td class="settings-item-value"><input type="text" class="styled device camera-config" id="deviceTypeEntry" disabled="disabled"></td>
</tr>
<tr class="settings-item advanced-setting">
<td colspan="100"><div class="settings-item-separator"></div></td>