Better handling of capture path and temporary files

This commit is contained in:
Joel Collins 2018-11-22 21:31:21 +00:00
parent afc75b809c
commit cf96fa0ad2
3 changed files with 46 additions and 31 deletions

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import time
import io
import os
import threading
from PIL import Image
import datetime
@ -14,7 +15,7 @@ except ImportError:
except ImportError:
from _thread import get_ident
from .capture import StreamObject
from .capture import StreamObject, BASE_CAPTURE_PATH
def last_entry(object_list: list):
@ -97,6 +98,10 @@ class BaseCamera(object):
self.state = {} #: dict: Dictionary for capture state
self.settings = {} #: dict: Dictionary of camera settings
self.paths = {
'image': BASE_CAPTURE_PATH,
'video': BASE_CAPTURE_PATH,
} #: dict: Dictionary of capture paths
# Capture data
self.images = [] #: list: List of image capture objects

View file

@ -9,18 +9,22 @@ from PIL import Image
pil_formats = ['JPG', 'JPEG', 'PNG', 'TIF', 'TIFF']
thumbnail_size = (60, 60)
BASE_CAPTURE_PATH = os.path.join(os.path.expanduser('~'), 'micrographs')
TEMP_CAPTURE_PATH = os.path.join(BASE_CAPTURE_PATH, 'tmp')
class StreamObject(object):
"""
StreamObject used to store and process capture data, and metadata.
Note: Captures cannot be stored in a lower-level directory than BASE_CAPTURE_PATH.
"""
def __init__(
self,
write_to_file: bool=False,
keep_on_disk: bool=True,
filename: str=None,
folder: str=None,
fmt: str='file') -> None:
filename: str='',
folder: str='',
fmt: str='') -> None:
"""Create a new StreamObject, to manage capture data."""
# Store a nice ID
self.id = uuid.uuid4().hex
@ -80,32 +84,35 @@ class StreamObject(object):
"""
Construct a full file path, based on filename, folder, and file format.
Defaults to datestamp.
Defaults to UUID.
"""
global TEMP_CAPTURE_PATH
base_name = filename
file_name = "{}.{}".format(base_name, fmt)
file_given_name = "{}.{}".format(filename, fmt) # Given file name. May include a relative path
self.file = os.path.join(folder, file_given_name) # 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
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))
# Move to tmp directory if not being kept
if not self.keep_on_disk:
# TODO: Empty tmp folder on module load and atexit
self.file_notmp = self.file # Store originally defined folder
self.filefolder = TEMP_CAPTURE_PATH
self.file = os.path.join(self.filefolder, self.filename)
# Create folder and file
if folder:
if not os.path.exists(folder):
os.mkdir(folder)
if not os.path.exists(self.filefolder):
os.mkdir(self.filefolder)
file_path = os.path.join(folder, file_name)
else:
file_path = file_name
# Handle path appendix
appendix = ""
if not self.keep_on_disk:
appendix += ".tmp"
file_path = file_path + appendix
#self.basename = filename
self.file = file_path
self.filename = file_name
self.basename = base_name
def split_file_path(self, filepath):
"""Takes a full file path, and splits it into separated class properties."""
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
def lock(self):
"""Set locked flag to True."""
@ -223,6 +230,13 @@ class StreamObject(object):
def save_file(self) -> bool:
"""Write the StreamObjects stream to a file."""
if not self.keep_on_disk: # If capture is currently temporary
self.load_file() # Load data from tmp file into stream, if tmp file exists
self.keep_on_disk = True # Flag as kept on disk
self.file = self.file_notmp # Reset file path to non-temporary path
self.split_file_path(self.file) # Set split properties based on new path
logging.info("Moved temporary file out to {}".format(self.file))
if self.stream_exists: # If there's a stream to save
with open(self.file, 'ab') as f: # Load file as bytes
logging.debug("Writing stream to file {}".format(self.file))

View file

@ -214,7 +214,6 @@ class StreamingCamera(BaseCamera):
target=None,
write_to_file: bool=True,
filename: str=None,
folder: str='record',
fmt: str='h264',
quality: int=15):
"""Start recording.
@ -225,7 +224,6 @@ class StreamingCamera(BaseCamera):
target (str/BytesIO): Target object to write bytes to.
write_to_file (bool/NoneType): Should the StreamObject write to a file?
filename (str): Name of the stored file. Defaults to timestamp.
folder (str): Relative directory to store data file in.
fmt (str): Format of the capture.
Returns:
@ -242,7 +240,7 @@ class StreamingCamera(BaseCamera):
StreamObject(
write_to_file=write_to_file,
filename=filename,
folder=folder,
folder=self.paths['video'],
fmt=fmt)
)
@ -339,7 +337,6 @@ class StreamingCamera(BaseCamera):
keep_on_disk: bool=True,
use_video_port: bool=False,
filename: str=None,
folder: str='capture',
fmt: str='jpeg',
resize: Tuple[int, int]=None):
"""
@ -353,7 +350,6 @@ class StreamingCamera(BaseCamera):
write_to_file (bool): Should the StreamObject write to a file, instead of BytesIO stream?
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
filename (str): Name of the stored file. Defaults to timestamp.
folder (str): Relative directory to store data file in.
fmt (str): Format of the capture.
resize ((int, int)): Resize the captured image.
"""
@ -372,7 +368,7 @@ class StreamingCamera(BaseCamera):
write_to_file=write_to_file,
keep_on_disk=keep_on_disk,
filename=filename,
folder=folder,
folder=self.paths['image'],
fmt=fmt)
)
target = target_obj.target # Store to the StreamObject BytesIO