Simplified creation of new capture objects
This commit is contained in:
parent
28561433a0
commit
ea44bbefd2
2 changed files with 37 additions and 85 deletions
|
|
@ -316,18 +316,20 @@ class BaseCamera(object):
|
||||||
if not filename:
|
if not filename:
|
||||||
filename = generate_numbered_basename(self.images)
|
filename = generate_numbered_basename(self.images)
|
||||||
logging.debug(filename)
|
logging.debug(filename)
|
||||||
|
filename = "{}.{}".format(filename, fmt)
|
||||||
|
|
||||||
# Generate folder
|
# Generate folder
|
||||||
base_folder = self.paths['image_tmp'] if temporary else self.paths['image']
|
base_folder = self.paths['image_tmp'] if temporary else self.paths['image']
|
||||||
folder = os.path.join(base_folder, folder)
|
folder = os.path.join(base_folder, folder)
|
||||||
|
|
||||||
|
# Generate file path
|
||||||
|
filepath = os.path.join(folder, filename)
|
||||||
|
|
||||||
# Create capture object
|
# Create capture object
|
||||||
output = CaptureObject(
|
output = CaptureObject(
|
||||||
write_to_file=write_to_file,
|
write_to_file=write_to_file,
|
||||||
temporary=temporary,
|
temporary=temporary,
|
||||||
filename=filename,
|
filepath=filepath)
|
||||||
folder=folder,
|
|
||||||
fmt=fmt)
|
|
||||||
|
|
||||||
# Update capture list
|
# Update capture list
|
||||||
self.shunt_captures(self.images)
|
self.shunt_captures(self.images)
|
||||||
|
|
@ -360,18 +362,20 @@ class BaseCamera(object):
|
||||||
if not filename:
|
if not filename:
|
||||||
filename = generate_numbered_basename(self.videos)
|
filename = generate_numbered_basename(self.videos)
|
||||||
logging.debug(filename)
|
logging.debug(filename)
|
||||||
|
filename = "{}.{}".format(filename, fmt)
|
||||||
|
|
||||||
# Generate folder
|
# Generate folder
|
||||||
base_folder = self.paths['video_tmp'] if temporary else self.paths['video']
|
base_folder = self.paths['video_tmp'] if temporary else self.paths['video']
|
||||||
folder = os.path.join(base_folder, folder)
|
folder = os.path.join(base_folder, folder)
|
||||||
|
|
||||||
|
# Generate file path
|
||||||
|
filepath = os.path.join(folder, filename)
|
||||||
|
|
||||||
# Create capture object
|
# Create capture object
|
||||||
output = CaptureObject(
|
output = CaptureObject(
|
||||||
write_to_file=write_to_file,
|
write_to_file=write_to_file,
|
||||||
temporary=temporary,
|
temporary=temporary,
|
||||||
filename=filename,
|
filepath=filepath)
|
||||||
folder=folder,
|
|
||||||
fmt=fmt)
|
|
||||||
|
|
||||||
# Update capture list
|
# Update capture list
|
||||||
self.shunt_captures(self.videos)
|
self.shunt_captures(self.videos)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ THUMBNAIL_SIZE = (200, 150)
|
||||||
BASE_CAPTURE_PATH = os.path.join(os.path.expanduser('~'), 'micrographs') #: str: Base path to store all captures
|
BASE_CAPTURE_PATH = os.path.join(os.path.expanduser('~'), 'micrographs') #: str: Base path to store all captures
|
||||||
TEMP_CAPTURE_PATH = os.path.join(BASE_CAPTURE_PATH, 'tmp') #: str: Base path to store all temporary captures (automatically emptied)
|
TEMP_CAPTURE_PATH = os.path.join(BASE_CAPTURE_PATH, 'tmp') #: str: Base path to store all temporary captures (automatically emptied)
|
||||||
|
|
||||||
|
# TODO: Move these methods to a camera utilities module?
|
||||||
def clear_tmp():
|
def clear_tmp():
|
||||||
"""
|
"""
|
||||||
Removes all files in the ``TEMP_CAPTURE_PATH`` directory
|
Removes all files in the ``TEMP_CAPTURE_PATH`` directory
|
||||||
|
|
@ -75,11 +75,9 @@ def capture_from_dict(capture_dict):
|
||||||
"""
|
"""
|
||||||
global EXIF_FORMATS
|
global EXIF_FORMATS
|
||||||
|
|
||||||
capture = CaptureObject() # Create a placeholder capture
|
capture = CaptureObject(
|
||||||
|
filepath=capture_dict['path']
|
||||||
# Get inherent capture information from database
|
) # Create a placeholder capture
|
||||||
capture.file = capture_dict['path']
|
|
||||||
capture.temporary = capture_dict['temporary']
|
|
||||||
capture.split_file_path(capture.file)
|
capture.split_file_path(capture.file)
|
||||||
|
|
||||||
if capture.format.upper() in EXIF_FORMATS:
|
if capture.format.upper() in EXIF_FORMATS:
|
||||||
|
|
@ -111,9 +109,7 @@ class CaptureObject(object):
|
||||||
self,
|
self,
|
||||||
write_to_file: bool = False,
|
write_to_file: bool = False,
|
||||||
temporary: bool = False,
|
temporary: bool = False,
|
||||||
filename: str = '',
|
filepath: str = '') -> None:
|
||||||
folder: str = '',
|
|
||||||
fmt: str = '') -> None:
|
|
||||||
"""Create a new StreamObject, to manage capture data."""
|
"""Create a new StreamObject, to manage capture data."""
|
||||||
|
|
||||||
# Store a nice ID
|
# Store a nice ID
|
||||||
|
|
@ -121,22 +117,15 @@ class CaptureObject(object):
|
||||||
logging.info("Created StreamObject {}".format(self.id))
|
logging.info("Created StreamObject {}".format(self.id))
|
||||||
self.timestring = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") #: str: Timestring of capture creation time
|
self.timestring = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") #: str: Timestring of capture creation time
|
||||||
|
|
||||||
# Store file format
|
|
||||||
self.format = fmt #: str: Capture data format (e.g. jpeg, h264)
|
|
||||||
|
|
||||||
# Keep on disk after close by default
|
# Keep on disk after close by default
|
||||||
self.temporary = temporary #: bool: Mark the capture as temporary, to be deleted as the server closes, or as resources are required
|
self.temporary = temporary #: bool: Mark the capture as temporary, to be deleted as the server closes, or as resources are required
|
||||||
|
|
||||||
# Explicitally state if capture should be written to a file, not a bytestream
|
|
||||||
self.write_to_file = write_to_file #: bool: Write capture data to disk immediately, rather than to memory initially (useful for video recordings)
|
|
||||||
|
|
||||||
# Create file name. Default to UUID
|
# Create file name. Default to UUID
|
||||||
if not filename:
|
if not filepath:
|
||||||
filename = self.id
|
self.file = self.build_file_path()
|
||||||
self.filename = "{}.{}".format(filename, fmt)
|
else:
|
||||||
|
self.file = filepath
|
||||||
# Create folder path
|
self.split_file_path(self.file)
|
||||||
self.folder = folder
|
|
||||||
|
|
||||||
# Dictionary for storing custom metadata
|
# Dictionary for storing custom metadata
|
||||||
self._metadata = {} #: dict: Dictionary of custom metadata to be included in metadata file
|
self._metadata = {} #: dict: Dictionary of custom metadata to be included in metadata file
|
||||||
|
|
@ -144,15 +133,18 @@ class CaptureObject(object):
|
||||||
# List for storing tags
|
# List for storing tags
|
||||||
self.tags = [] #: list: List of tags. Essentially just as extra custom metadata field, but useful for quick organisation
|
self.tags = [] #: list: List of tags. Essentially just as extra custom metadata field, but useful for quick organisation
|
||||||
|
|
||||||
# Initialise the capture stream
|
# Byte bytestream properties
|
||||||
self.initialise_capture()
|
self.bytestream = io.BytesIO() # Byte bytestream that data will be written to
|
||||||
|
|
||||||
|
# Set default write target
|
||||||
|
if not write_to_file:
|
||||||
|
self.stream = self.bytestream
|
||||||
|
else:
|
||||||
|
self.stream = self.file
|
||||||
|
|
||||||
# Log if created by context manager
|
# Log if created by context manager
|
||||||
self.context_manager = False
|
self.context_manager = False
|
||||||
|
|
||||||
# Object lock
|
|
||||||
self.locked = False
|
|
||||||
|
|
||||||
# Thumbnail (populated only for PIL captures)
|
# Thumbnail (populated only for PIL captures)
|
||||||
self.thumb_bytes = None
|
self.thumb_bytes = None
|
||||||
|
|
||||||
|
|
@ -165,7 +157,7 @@ class CaptureObject(object):
|
||||||
self.context_manager = True # Used in metadata
|
self.context_manager = True # Used in metadata
|
||||||
|
|
||||||
logging.info("Rebuilding as a temporary capture...")
|
logging.info("Rebuilding as a temporary capture...")
|
||||||
self.initialise_capture()
|
self.build_file_path()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
@ -174,52 +166,13 @@ class CaptureObject(object):
|
||||||
logging.info("Cleaning up {}".format(self.id))
|
logging.info("Cleaning up {}".format(self.id))
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def initialise_capture(self):
|
|
||||||
"""
|
|
||||||
Initialise the capture object. Creates a file name and builds a file path,
|
|
||||||
creates an in-memory byte stream for capture data, and creates a metadata
|
|
||||||
file on disk.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.build_file_path()
|
|
||||||
|
|
||||||
# Byte bytestream properties
|
|
||||||
self.bytestream = io.BytesIO() # Byte bytestream that data will be written to
|
|
||||||
|
|
||||||
# Set default write target
|
|
||||||
if not self.write_to_file:
|
|
||||||
logging.debug("Target for {} set to 'bytestream'".format(self.id))
|
|
||||||
self.stream = self.bytestream
|
|
||||||
else:
|
|
||||||
logging.debug("Target for {} set to 'file'".format(self.id))
|
|
||||||
self.stream = self.file
|
|
||||||
|
|
||||||
# Save initial metadata file
|
|
||||||
self.save_metadata()
|
|
||||||
|
|
||||||
def build_file_path(self):
|
def build_file_path(self):
|
||||||
"""
|
"""
|
||||||
Construct a full file path, based on filename, folder, and file format.
|
Construct a full file path, based on filename, folder, and file format.
|
||||||
Defaults to UUID.
|
Defaults to UUID.
|
||||||
"""
|
"""
|
||||||
global TEMP_CAPTURE_PATH
|
global TEMP_CAPTURE_PATH
|
||||||
# TODO: Combine this and split_file_path, and tidy. Let the device (camera) handle folders. This should be more basic.
|
return os.path.join(self.folder, self.filename) # Full file name by joining given folder to given name
|
||||||
# TODO: Even let the base camera manage moving captures to temp folder
|
|
||||||
# This module will clear out the temp folder, but won't MOVE anything there.
|
|
||||||
# In Base cameras new_image method, the full folder is constructed. Temp folder should be inserted there.
|
|
||||||
|
|
||||||
self.file = os.path.join(self.folder, self.filename) # Full file name by joining given folder to given name
|
|
||||||
|
|
||||||
self.split_file_path(self.file) # Split file path into folder, filename, and basename
|
|
||||||
|
|
||||||
# Check directory is a subdirectory of BASE_CAPTURE_PATH
|
|
||||||
# TODO: Do we need this?
|
|
||||||
if not os.path.commonprefix([self.file, BASE_CAPTURE_PATH]) == BASE_CAPTURE_PATH:
|
|
||||||
raise Exception("Captures cannot be stored in a lower-level directory than {}.".format(BASE_CAPTURE_PATH))
|
|
||||||
|
|
||||||
# Create folder and file
|
|
||||||
if not os.path.exists(self.filefolder):
|
|
||||||
os.makedirs(self.filefolder)
|
|
||||||
|
|
||||||
def split_file_path(self, filepath):
|
def split_file_path(self, filepath):
|
||||||
"""
|
"""
|
||||||
|
|
@ -230,17 +183,11 @@ class CaptureObject(object):
|
||||||
"""
|
"""
|
||||||
self.filefolder, self.filename = os.path.split(filepath) # Split the full file path into a folder and a filename
|
self.filefolder, self.filename = os.path.split(filepath) # Split the full file path into a folder and a filename
|
||||||
self.basename = os.path.splitext(self.filename)[0] # Split the filename out from it's file extension
|
self.basename = os.path.splitext(self.filename)[0] # Split the filename out from it's file extension
|
||||||
|
self.format = self.filename.split('.')[-1]
|
||||||
|
|
||||||
if not self.format:
|
# Create folder and file
|
||||||
self.format = self.filename.split('.')[-1]
|
if not os.path.exists(self.filefolder):
|
||||||
|
os.makedirs(self.filefolder)
|
||||||
def lock(self):
|
|
||||||
"""Set locked flag to True."""
|
|
||||||
self.locked = True
|
|
||||||
|
|
||||||
def unlock(self):
|
|
||||||
"""Set locked flag to False."""
|
|
||||||
self.locked = False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stream_exists(self, auto_rewind=True) -> bool:
|
def stream_exists(self, auto_rewind=True) -> bool:
|
||||||
|
|
@ -325,7 +272,7 @@ class CaptureObject(object):
|
||||||
Create basic metadata dictionary from basic capture data,
|
Create basic metadata dictionary from basic capture data,
|
||||||
and any added custom metadata and tags.
|
and any added custom metadata and tags.
|
||||||
"""
|
"""
|
||||||
d = {'id': self.id, 'filename': self.filename, 'path': self.file, 'time': self.timestring,
|
d = {'id': self.id, 'filename': self.filename, 'time': self.timestring,
|
||||||
'format': self.format, 'tags': self.tags, 'custom': self._metadata}
|
'format': self.format, 'tags': self.tags, 'custom': self._metadata}
|
||||||
|
|
||||||
# Add custom metadata to dictionary
|
# Add custom metadata to dictionary
|
||||||
|
|
@ -354,7 +301,7 @@ class CaptureObject(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Create basic state dictionary
|
# Create basic state dictionary
|
||||||
d = {'path': self.file, 'locked': self.locked, 'temporary': self.temporary, 'metadata': self.metadata}
|
d = {'path': self.file, 'temporary': self.temporary, 'metadata': self.metadata}
|
||||||
|
|
||||||
# Check bytestream
|
# Check bytestream
|
||||||
if self.stream_exists:
|
if self.stream_exists:
|
||||||
|
|
@ -382,6 +329,7 @@ class CaptureObject(object):
|
||||||
|
|
||||||
if self.stream_exists: # If data bytestream contains data
|
if self.stream_exists: # If data bytestream contains data
|
||||||
# Create a copy of the bytestream bytes
|
# Create a copy of the bytestream bytes
|
||||||
|
logging.debug("STREAM EXISTS")
|
||||||
data = io.BytesIO(self.bytestream.getbuffer())
|
data = io.BytesIO(self.bytestream.getbuffer())
|
||||||
|
|
||||||
else: # If data bytestream is empty
|
else: # If data bytestream is empty
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue