Merge pull request #362 from Glandos/performance_one_open_for_metadata
Performance: Read all metadata at once
This commit is contained in:
@@ -40,8 +40,7 @@ from urllib.parse import quote as url_quote
|
||||
from click import get_terminal_size, progressbar
|
||||
|
||||
from . import image, signals, video
|
||||
from .image import (get_exif_data, get_exif_tags, get_iptc_data, get_size,
|
||||
process_image)
|
||||
from .image import get_exif_tags, get_image_metadata, get_size, process_image
|
||||
from .settings import get_thumb
|
||||
from .utils import (Devnull, cached_property, check_or_create_dir, copy,
|
||||
get_mime, is_valid_html5_video, read_markdown,
|
||||
@@ -84,7 +83,10 @@ class Media:
|
||||
self.thumb_path = join(settings['destination'], path, self.thumb_name)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
self.file_metadata = None
|
||||
self._get_metadata()
|
||||
|
||||
# default: title is the filename
|
||||
if not self.title:
|
||||
self.title = self.filename
|
||||
@@ -196,32 +198,25 @@ class Image(Media):
|
||||
|
||||
def _get_metadata(self):
|
||||
super()._get_metadata()
|
||||
self.file_metadata = get_image_metadata(self.src_path)
|
||||
|
||||
# If a title or description hasn't been obtained by other means, look
|
||||
# for the information in IPTC fields
|
||||
if self.title and self.description:
|
||||
# Nothing to do - we already have title and description
|
||||
return
|
||||
|
||||
try:
|
||||
iptc_data = get_iptc_data(self.src_path)
|
||||
except Exception as e:
|
||||
self.logger.warning('Could not read IPTC data from %s: %s',
|
||||
self.src_path, e)
|
||||
else:
|
||||
if not self.title and iptc_data.get('title'):
|
||||
self.title = iptc_data['title']
|
||||
if not self.description and iptc_data.get('description'):
|
||||
self.description = iptc_data['description']
|
||||
iptc_data = self.file_metadata['iptc']
|
||||
if not self.title and iptc_data.get('title'):
|
||||
self.title = iptc_data['title']
|
||||
if not self.description and iptc_data.get('description'):
|
||||
self.description = iptc_data['description']
|
||||
|
||||
@cached_property
|
||||
def raw_exif(self):
|
||||
"""If not `None`, contains the raw EXIF tags."""
|
||||
try:
|
||||
return (get_exif_data(self.src_path)
|
||||
if self.ext in ('.jpg', '.jpeg') else None)
|
||||
except Exception as e:
|
||||
self.logger.warning('Could not read EXIF data from %s: %s',
|
||||
self.src_path, e)
|
||||
if self.ext in ('.jpg', '.jpeg'):
|
||||
return self.file_metadata['exif']
|
||||
|
||||
@cached_property
|
||||
def size(self):
|
||||
@@ -460,7 +455,7 @@ class Album:
|
||||
# fallback to the size of src_path if dst_path is missing
|
||||
size = f.size
|
||||
if size is None:
|
||||
size = get_size(f.src_path)
|
||||
size = f.file_metadata['size']
|
||||
|
||||
if size['width'] > size['height']:
|
||||
self._thumbnail = (url_quote(self.name) + '/' +
|
||||
|
||||
@@ -56,6 +56,10 @@ def _has_exif_tags(img):
|
||||
|
||||
|
||||
def _read_image(file_path):
|
||||
# The image is already opened
|
||||
if isinstance(file_path, PILImage.Image):
|
||||
return file_path
|
||||
|
||||
with warnings.catch_warnings(record=True) as caught_warnings:
|
||||
im = PILImage.open(file_path)
|
||||
|
||||
@@ -203,10 +207,7 @@ def get_size(file_path):
|
||||
logger.error("Could not read size of %s due to %r", file_path, e)
|
||||
else:
|
||||
width, height = im.size
|
||||
return {
|
||||
'width': width,
|
||||
'height': height
|
||||
}
|
||||
return {'width': width, 'height': height}
|
||||
|
||||
|
||||
def get_exif_data(filename):
|
||||
@@ -271,6 +272,34 @@ def get_iptc_data(filename):
|
||||
return iptc_data
|
||||
|
||||
|
||||
def get_image_metadata(filename):
|
||||
logger = logging.getLogger(__name__)
|
||||
exif, iptc, size = {}, {}, {}
|
||||
|
||||
try:
|
||||
img = _read_image(filename)
|
||||
except Exception as e:
|
||||
logger.error('Could not open image %s metadata: %s', filename, e)
|
||||
else:
|
||||
try:
|
||||
if os.path.splitext(filename)[1].lower() in ('.jpg', '.jpeg'):
|
||||
exif = get_exif_data(img)
|
||||
except Exception as e:
|
||||
logger.warning('Could not read EXIF data from %s: %s', filename, e)
|
||||
|
||||
try:
|
||||
iptc = get_iptc_data(img)
|
||||
except Exception as e:
|
||||
logger.warning('Could not read IPTC data from %s: %s', filename, e)
|
||||
|
||||
try:
|
||||
size = get_size(img)
|
||||
except Exception as e:
|
||||
logger.warning('Could not read size from %s: %s', filename, e)
|
||||
|
||||
return {'exif': exif, 'iptc': iptc, 'size': size}
|
||||
|
||||
|
||||
def dms_to_degrees(v):
|
||||
"""Convert degree/minute/second to decimal degrees."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user