From 89e3edf2f5442d8d319ab1615e98f0d41abbf529 Mon Sep 17 00:00:00 2001 From: Glandos Date: Thu, 13 Dec 2018 22:37:13 +0100 Subject: [PATCH 1/3] Read all metadata at once. Make _read_image no-op if argument is an image. Add gathering metadata function that returns a dict --- sigal/gallery.py | 10 +++++++--- sigal/image.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/sigal/gallery.py b/sigal/gallery.py index 7e8ac12..4a3f289 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -41,7 +41,7 @@ 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) + process_image, get_image_metadata) from .settings import get_thumb from .utils import (Devnull, cached_property, check_or_create_dir, copy, get_mime, is_valid_html5_video, read_markdown, @@ -156,6 +156,8 @@ class Media: self.description = '' """Description extracted from the Markdown .md file.""" + self.file_metadata = {} + self.title = '' """Title extracted from the Markdown .md file.""" @@ -196,6 +198,8 @@ class Image(Media): def _get_metadata(self): super()._get_metadata() + src_metadata = get_image_metadata(self.src_path) + self.file_metadata[self.src_path] = src_metadata # 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: @@ -203,7 +207,7 @@ class Image(Media): return try: - iptc_data = get_iptc_data(self.src_path) + iptc_data = src_metadata.get('iptc', {}) except Exception as e: self.logger.warning('Could not read IPTC data from %s: %s', self.src_path, e) @@ -217,7 +221,7 @@ class Image(Media): def raw_exif(self): """If not `None`, contains the raw EXIF tags.""" try: - return (get_exif_data(self.src_path) + return (self.file_metadata[self.src_path]['exif'] if self.ext in ('.jpg', '.jpeg') else None) except Exception as e: self.logger.warning('Could not read EXIF data from %s: %s', diff --git a/sigal/image.py b/sigal/image.py index b1a4066..67fc1a7 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -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) @@ -271,6 +275,43 @@ def get_iptc_data(filename): return iptc_data +def get_image_metadata(filename): + logger = logging.getLogger(__name__) + try: + img = _read_image(filename) + except Exception as e: + logger.error('Could not open image %s metadata: %s', + filename, e) + + exif = None + iptc = None + size = None + + try: + 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.""" From 18903fd477e0092056e9e9ac6f585819ea4bcf7a Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 15 Mar 2020 23:58:01 -0300 Subject: [PATCH 2/3] Simplify a bit --- sigal/gallery.py | 38 +++++++++++++++----------------------- sigal/image.py | 33 +++++++++++---------------------- 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/sigal/gallery.py b/sigal/gallery.py index 4a3f289..ae3330e 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -40,8 +40,8 @@ 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, get_image_metadata) +from .image import (get_exif_data, get_exif_tags, get_image_metadata, + get_iptc_data, 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 +84,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 @@ -156,8 +159,6 @@ class Media: self.description = '' """Description extracted from the Markdown .md file.""" - self.file_metadata = {} - self.title = '' """Title extracted from the Markdown .md file.""" @@ -198,34 +199,25 @@ class Image(Media): def _get_metadata(self): super()._get_metadata() - src_metadata = get_image_metadata(self.src_path) - self.file_metadata[self.src_path] = src_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 = src_metadata.get('iptc', {}) - 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 (self.file_metadata[self.src_path]['exif'] - 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): @@ -464,7 +456,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) + '/' + diff --git a/sigal/image.py b/sigal/image.py index 67fc1a7..e231840 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -207,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): @@ -280,36 +277,28 @@ def get_image_metadata(filename): try: img = _read_image(filename) except Exception as e: - logger.error('Could not open image %s metadata: %s', - filename, e) - - exif = None - iptc = None - size = None + logger.error('Could not open image %s metadata: %s', filename, e) + return {'exif': None, 'iptc': None, 'size': None} try: exif = get_exif_data(img) except Exception as e: - logger.warning('Could not read EXIF data from %s: %s', - filename, e) + logger.warning('Could not read EXIF data from %s: %s', filename, e) + exif = {} try: iptc = get_iptc_data(img) except Exception as e: - logger.warning('Could not read IPTC data from %s: %s', - filename, e) + logger.warning('Could not read IPTC data from %s: %s', filename, e) + iptc = {} 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, - } + logger.warning('Could not read size from %s: %s', filename, e) + size = {} + + return {'exif': exif, 'iptc': iptc, 'size': size} def dms_to_degrees(v): From ef87f6ad9c18df9886e1543c923fc1d59b60164f Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 16 Mar 2020 00:06:58 -0300 Subject: [PATCH 3/3] Get EXIF only for JPG files --- sigal/gallery.py | 3 +-- sigal/image.py | 33 ++++++++++++++++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/sigal/gallery.py b/sigal/gallery.py index ae3330e..8cf7f71 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -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_image_metadata, - 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, diff --git a/sigal/image.py b/sigal/image.py index e231840..04a0e13 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -274,29 +274,28 @@ def get_iptc_data(filename): 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) - return {'exif': None, 'iptc': None, 'size': None} - - try: - exif = get_exif_data(img) - except Exception as e: - logger.warning('Could not read EXIF data from %s: %s', filename, e) - exif = {} + 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) - iptc = {} + 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) - size = {} + 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}