Better handling of capture path and temporary files
This commit is contained in:
parent
afc75b809c
commit
cf96fa0ad2
3 changed files with 46 additions and 31 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import datetime
|
import datetime
|
||||||
|
|
@ -14,7 +15,7 @@ except ImportError:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from _thread import get_ident
|
from _thread import get_ident
|
||||||
|
|
||||||
from .capture import StreamObject
|
from .capture import StreamObject, BASE_CAPTURE_PATH
|
||||||
|
|
||||||
|
|
||||||
def last_entry(object_list: list):
|
def last_entry(object_list: list):
|
||||||
|
|
@ -97,6 +98,10 @@ class BaseCamera(object):
|
||||||
|
|
||||||
self.state = {} #: dict: Dictionary for capture state
|
self.state = {} #: dict: Dictionary for capture state
|
||||||
self.settings = {} #: dict: Dictionary of camera settings
|
self.settings = {} #: dict: Dictionary of camera settings
|
||||||
|
self.paths = {
|
||||||
|
'image': BASE_CAPTURE_PATH,
|
||||||
|
'video': BASE_CAPTURE_PATH,
|
||||||
|
} #: dict: Dictionary of capture paths
|
||||||
|
|
||||||
# Capture data
|
# Capture data
|
||||||
self.images = [] #: list: List of image capture objects
|
self.images = [] #: list: List of image capture objects
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,22 @@ from PIL import Image
|
||||||
pil_formats = ['JPG', 'JPEG', 'PNG', 'TIF', 'TIFF']
|
pil_formats = ['JPG', 'JPEG', 'PNG', 'TIF', 'TIFF']
|
||||||
thumbnail_size = (60, 60)
|
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):
|
class StreamObject(object):
|
||||||
"""
|
"""
|
||||||
StreamObject used to store and process capture data, and metadata.
|
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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
write_to_file: bool=False,
|
write_to_file: bool=False,
|
||||||
keep_on_disk: bool=True,
|
keep_on_disk: bool=True,
|
||||||
filename: str=None,
|
filename: str='',
|
||||||
folder: str=None,
|
folder: str='',
|
||||||
fmt: str='file') -> None:
|
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
|
||||||
self.id = uuid.uuid4().hex
|
self.id = uuid.uuid4().hex
|
||||||
|
|
@ -80,32 +84,35 @@ class StreamObject(object):
|
||||||
"""
|
"""
|
||||||
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 datestamp.
|
Defaults to UUID.
|
||||||
"""
|
"""
|
||||||
|
global TEMP_CAPTURE_PATH
|
||||||
|
|
||||||
base_name = filename
|
file_given_name = "{}.{}".format(filename, fmt) # Given file name. May include a relative path
|
||||||
file_name = "{}.{}".format(base_name, fmt)
|
|
||||||
|
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
|
# Create folder and file
|
||||||
if folder:
|
if not os.path.exists(self.filefolder):
|
||||||
if not os.path.exists(folder):
|
os.mkdir(self.filefolder)
|
||||||
os.mkdir(folder)
|
|
||||||
|
|
||||||
file_path = os.path.join(folder, file_name)
|
def split_file_path(self, filepath):
|
||||||
else:
|
"""Takes a full file path, and splits it into separated class properties."""
|
||||||
file_path = file_name
|
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
|
||||||
# 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 lock(self):
|
def lock(self):
|
||||||
"""Set locked flag to True."""
|
"""Set locked flag to True."""
|
||||||
|
|
@ -223,6 +230,13 @@ class StreamObject(object):
|
||||||
|
|
||||||
def save_file(self) -> bool:
|
def save_file(self) -> bool:
|
||||||
"""Write the StreamObjects stream to a file."""
|
"""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
|
if self.stream_exists: # If there's a stream to save
|
||||||
with open(self.file, 'ab') as f: # Load file as bytes
|
with open(self.file, 'ab') as f: # Load file as bytes
|
||||||
logging.debug("Writing stream to file {}".format(self.file))
|
logging.debug("Writing stream to file {}".format(self.file))
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,6 @@ class StreamingCamera(BaseCamera):
|
||||||
target=None,
|
target=None,
|
||||||
write_to_file: bool=True,
|
write_to_file: bool=True,
|
||||||
filename: str=None,
|
filename: str=None,
|
||||||
folder: str='record',
|
|
||||||
fmt: str='h264',
|
fmt: str='h264',
|
||||||
quality: int=15):
|
quality: int=15):
|
||||||
"""Start recording.
|
"""Start recording.
|
||||||
|
|
@ -225,7 +224,6 @@ class StreamingCamera(BaseCamera):
|
||||||
target (str/BytesIO): Target object to write bytes to.
|
target (str/BytesIO): Target object to write bytes to.
|
||||||
write_to_file (bool/NoneType): Should the StreamObject write to a file?
|
write_to_file (bool/NoneType): Should the StreamObject write to a file?
|
||||||
filename (str): Name of the stored file. Defaults to timestamp.
|
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.
|
fmt (str): Format of the capture.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -242,7 +240,7 @@ class StreamingCamera(BaseCamera):
|
||||||
StreamObject(
|
StreamObject(
|
||||||
write_to_file=write_to_file,
|
write_to_file=write_to_file,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
folder=folder,
|
folder=self.paths['video'],
|
||||||
fmt=fmt)
|
fmt=fmt)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -339,7 +337,6 @@ class StreamingCamera(BaseCamera):
|
||||||
keep_on_disk: bool=True,
|
keep_on_disk: bool=True,
|
||||||
use_video_port: bool=False,
|
use_video_port: bool=False,
|
||||||
filename: str=None,
|
filename: str=None,
|
||||||
folder: str='capture',
|
|
||||||
fmt: str='jpeg',
|
fmt: str='jpeg',
|
||||||
resize: Tuple[int, int]=None):
|
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?
|
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.
|
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.
|
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.
|
fmt (str): Format of the capture.
|
||||||
resize ((int, int)): Resize the captured image.
|
resize ((int, int)): Resize the captured image.
|
||||||
"""
|
"""
|
||||||
|
|
@ -372,7 +368,7 @@ class StreamingCamera(BaseCamera):
|
||||||
write_to_file=write_to_file,
|
write_to_file=write_to_file,
|
||||||
keep_on_disk=keep_on_disk,
|
keep_on_disk=keep_on_disk,
|
||||||
filename=filename,
|
filename=filename,
|
||||||
folder=folder,
|
folder=self.paths['image'],
|
||||||
fmt=fmt)
|
fmt=fmt)
|
||||||
)
|
)
|
||||||
target = target_obj.target # Store to the StreamObject BytesIO
|
target = target_obj.target # Store to the StreamObject BytesIO
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue