Initial testing for exif metadata
This commit is contained in:
parent
2314084cb1
commit
340b3dda89
2 changed files with 59 additions and 56 deletions
|
|
@ -248,17 +248,6 @@ class BaseCamera(object):
|
|||
for capture in capture_list:
|
||||
if capture.file_exists:
|
||||
valid_captures.append(capture)
|
||||
else:
|
||||
if os.path.isfile(capture.metadata_file):
|
||||
logging.warning("Capture data missing. Deleting metadata file {}.".format(capture.metadata_file))
|
||||
os.remove(capture.metadata_file)
|
||||
|
||||
# Validate and repair captures
|
||||
for capture in valid_captures:
|
||||
# If the captures metadata file is missing
|
||||
if not os.path.isfile(capture.metadata_file):
|
||||
# Recreate the metadata file
|
||||
capture.save_metadata()
|
||||
|
||||
return valid_captures
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import logging
|
|||
from PIL import Image
|
||||
import atexit
|
||||
|
||||
import piexif
|
||||
|
||||
pil_formats = ['JPG', 'JPEG', 'PNG', 'TIF', 'TIFF']
|
||||
thumbnail_size = (200, 150)
|
||||
|
||||
|
|
@ -30,6 +32,30 @@ def clear_tmp():
|
|||
logging.debug("Cleared {}.".format(TEMP_CAPTURE_PATH))
|
||||
|
||||
|
||||
def pull_usercomment_dict(filepath):
|
||||
"""
|
||||
Reads UserComment Exif data from a file, and returns the contained bytes as a dictionary.
|
||||
Args:
|
||||
filepath: Path to the Exif-containing file
|
||||
"""
|
||||
exif_dict = piexif.load(filepath)
|
||||
if 'Exif' in exif_dict and 37510 in exif_dict['Exif']:
|
||||
return yaml.load(exif_dict['Exif'][37510].decode())
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def extract_with_priority(key, best_dict, backup_dict):
|
||||
if key in best_dict:
|
||||
return best_dict[key]
|
||||
elif key in backup_dict:
|
||||
logging.warning("Key {} not found in primary dictionary. Falling back to backup.".format(key))
|
||||
return backup_dict[key]
|
||||
else:
|
||||
logging.error("Key {} not found in either dictionary!".format(key))
|
||||
return None
|
||||
|
||||
|
||||
def capture_from_dict(capture_dict):
|
||||
"""
|
||||
Creates an instance of CaptureObject from a dictionary of capture information.
|
||||
|
|
@ -39,22 +65,24 @@ def capture_from_dict(capture_dict):
|
|||
Args:
|
||||
capture_dict (dict): Dictionary containing capture information
|
||||
"""
|
||||
capture = CaptureObject(create_metadata_file=False) # Create a placeholder capture
|
||||
|
||||
capture = CaptureObject() # Create a placeholder capture
|
||||
|
||||
# Get inherent capture information from database
|
||||
capture.file = capture_dict['path']
|
||||
capture.split_file_path(capture.file)
|
||||
capture.temporary = capture_dict['temporary']
|
||||
|
||||
md_exif = pull_usercomment_dict(capture.file)
|
||||
md_database = capture_dict['metadata']
|
||||
|
||||
# Populate capture parameters
|
||||
capture.temporary = capture_dict['temporary']
|
||||
capture.id = capture_dict['metadata']['id']
|
||||
capture.timestring = capture_dict['metadata']['time']
|
||||
capture.format = capture_dict['metadata']['format']
|
||||
capture.id = extract_with_priority('id', md_exif, md_database)
|
||||
capture.timestring = extract_with_priority('time', md_exif, md_database)
|
||||
capture.format = extract_with_priority('format', md_exif, md_database)
|
||||
|
||||
if 'custom' in capture_dict['metadata']:
|
||||
capture._metadata = capture_dict['metadata']['custom']
|
||||
|
||||
if 'tags' in capture_dict['metadata']:
|
||||
capture.tags = capture_dict['metadata']['tags']
|
||||
|
||||
capture.file = capture_dict['metadata']['path']
|
||||
capture.split_file_path(capture.file)
|
||||
capture._metadata = extract_with_priority('custom', md_exif, md_database)
|
||||
capture.tags = extract_with_priority('tags', md_exif, md_database)
|
||||
|
||||
return capture
|
||||
|
||||
|
|
@ -72,8 +100,7 @@ class CaptureObject(object):
|
|||
temporary: bool = False,
|
||||
filename: str = '',
|
||||
folder: str = '',
|
||||
fmt: str = '',
|
||||
create_metadata_file: bool = True) -> None:
|
||||
fmt: str = '') -> None:
|
||||
"""Create a new StreamObject, to manage capture data."""
|
||||
|
||||
# Store a nice ID
|
||||
|
|
@ -107,7 +134,7 @@ class CaptureObject(object):
|
|||
self.tags = [] #: list: List of tags. Essentially just as extra custom metadata field, but useful for quick organisation
|
||||
|
||||
# Initialise the capture stream
|
||||
self.initialise_capture(create_metadata_file=create_metadata_file)
|
||||
self.initialise_capture()
|
||||
|
||||
# Log if created by context manager
|
||||
self.context_manager = False
|
||||
|
|
@ -136,14 +163,12 @@ class CaptureObject(object):
|
|||
logging.info("Cleaning up {}".format(self.id))
|
||||
self.close()
|
||||
|
||||
def initialise_capture(self, create_metadata_file=True):
|
||||
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.
|
||||
|
||||
Args:
|
||||
create_metadata_file (bool): Skips creating a metadata file if False (useful for captures being processed but not stored)
|
||||
"""
|
||||
self.build_file_path(self.filename, self.folder)
|
||||
|
||||
|
|
@ -159,8 +184,7 @@ class CaptureObject(object):
|
|||
self.stream = self.file
|
||||
|
||||
# Save initial metadata file
|
||||
if create_metadata_file:
|
||||
self.save_metadata()
|
||||
self.save_metadata()
|
||||
|
||||
def build_file_path(
|
||||
self,
|
||||
|
|
@ -263,13 +287,6 @@ class CaptureObject(object):
|
|||
|
||||
# HANDLE METADATA
|
||||
|
||||
@property
|
||||
def metadata_file(self) -> str:
|
||||
"""
|
||||
Path to the associated on-disk metadata YAML file.
|
||||
"""
|
||||
return "{}.yaml".format(os.path.splitext(self.file)[0])
|
||||
|
||||
def put_metadata(self, data: dict) -> None:
|
||||
"""
|
||||
Merge metadata from a passed dictionary into the capture metadata, and saves.
|
||||
|
|
@ -282,19 +299,21 @@ class CaptureObject(object):
|
|||
|
||||
def save_metadata(self) -> None:
|
||||
"""
|
||||
Save metadata to on-disk metadata YAML file
|
||||
Save metadata to exif
|
||||
"""
|
||||
logging.debug("Writing metadata to file {}".format(self.metadata_file))
|
||||
with open(self.metadata_file, 'w') as outfile:
|
||||
yaml.dump(self.metadata, outfile, default_flow_style=False)
|
||||
|
||||
def delete_metadata(self) -> None:
|
||||
"""
|
||||
Delete on-disk metadata YAML file, if it exists
|
||||
"""
|
||||
if os.path.isfile(self.metadata_file):
|
||||
logging.info("Deleting file {}".format(self.metadata_file))
|
||||
os.remove(self.metadata_file)
|
||||
if self.file_exists:
|
||||
logging.debug("Writing exif data to capture file")
|
||||
# Extract current Exif data
|
||||
exif_dict = piexif.load(self.file)
|
||||
# Serialize metadata
|
||||
metadata_string = yaml.safe_dump(self.metadata)
|
||||
# Insert metadata into exif_dict
|
||||
exif_dict['Exif'][piexif.ExifIFD.UserComment] = metadata_string.encode()
|
||||
# Convert new exif dict to exif bytes
|
||||
exif_bytes = piexif.dump(exif_dict)
|
||||
# Insert exif into file
|
||||
piexif.insert(exif_bytes, self.file)
|
||||
|
||||
@property
|
||||
def metadata(self) -> dict:
|
||||
|
|
@ -331,8 +350,7 @@ class CaptureObject(object):
|
|||
"""
|
||||
|
||||
# Create basic state dictionary
|
||||
d = {'locked': self.locked, 'temporary': self.temporary, 'metadata': self.metadata,
|
||||
'metadata_path': self.metadata_file}
|
||||
d = {'path': self.file, 'locked': self.locked, 'temporary': self.temporary, 'metadata': self.metadata}
|
||||
|
||||
# Check bytestream
|
||||
if self.stream_exists:
|
||||
|
|
@ -445,9 +463,6 @@ class CaptureObject(object):
|
|||
|
||||
def delete_file(self) -> bool:
|
||||
"""If the StreamObject has been saved, delete the file."""
|
||||
if os.path.isfile(self.metadata_file):
|
||||
logging.info("Deleting file {}".format(self.metadata_file))
|
||||
os.remove(self.metadata_file)
|
||||
|
||||
if os.path.isfile(self.file):
|
||||
logging.info("Deleting file {}".format(self.file))
|
||||
|
|
@ -462,7 +477,6 @@ class CaptureObject(object):
|
|||
logging.info("Deleting {}".format(self.id))
|
||||
self.delete_stream()
|
||||
self.delete_file()
|
||||
self.delete_metadata()
|
||||
|
||||
def shunt(self):
|
||||
"""Demote the StreamObject from being stored in memory."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue