diff --git a/src/sigal/gallery.py b/src/sigal/gallery.py index 8ca5e01..addddb2 100644 --- a/src/sigal/gallery.py +++ b/src/sigal/gallery.py @@ -45,7 +45,13 @@ from natsort import natsort_keygen, ns from PIL import Image as PILImage from . import image, signals, video -from .image import get_exif_tags, get_image_metadata, get_size, process_image +from .image import ( + EXIF_EXTENSIONS, + get_exif_tags, + get_image_metadata, + get_size, + process_image, +) from .settings import Status, get_thumb from .utils import ( Devnull, @@ -264,7 +270,7 @@ class Image(Media): datetime_format = self.settings["datetime_format"] return ( get_exif_tags(self.raw_exif, datetime_format=datetime_format) - if self.raw_exif and self.src_ext in (".jpg", ".jpeg", ".heic") + if self.raw_exif and self.src_ext in EXIF_EXTENSIONS else None ) @@ -289,7 +295,7 @@ class Image(Media): @cached_property def raw_exif(self): """If not `None`, contains the raw EXIF tags.""" - if self.src_ext in (".jpg", ".jpeg", ".heic"): + if self.src_ext in EXIF_EXTENSIONS: return self.file_metadata["exif"] @cached_property diff --git a/src/sigal/image.py b/src/sigal/image.py index 4996125..f7d154b 100644 --- a/src/sigal/image.py +++ b/src/sigal/image.py @@ -45,15 +45,20 @@ from pilkit.processors import Transpose from pilkit.utils import save_image try: - from pillow_heif import HeifImagePlugin -except: - pass + from pillow_heif import HeifImagePlugin # noqa: F401 + + HAS_HEIF = True +except ImportError: + HAS_HEIF = False from . import signals, utils +from .settings import Status # Force loading of truncated files ImageFile.LOAD_TRUNCATED_IMAGES = True +EXIF_EXTENSIONS = (".jpg", ".jpeg", ".heic") + def _has_exif_tags(img): return hasattr(img, "info") and "exif" in img.info @@ -71,7 +76,7 @@ def _read_image(file_path): for w in caught_warnings: logger.warning( - f"PILImage reported a warning for file {file_path}\n" + f"Pillow reported a warning for file {file_path}\n" f"{w.category}: {w.message}" ) return im @@ -185,6 +190,11 @@ def process_image(media): options = media.settings["jpg_options"] elif media.src_ext == ".png": options = {"optimize": True} + elif media.src_ext == ".heic" and not HAS_HEIF: + logger.warning( + f"cannot open {media.src_path}, pillow-heif is needed to open .heic files" + ) + return Status.FAILURE else: options = {} @@ -298,7 +308,7 @@ def get_image_metadata(filename): logger.error("Could not open image %s metadata: %s", filename, e) else: try: - if os.path.splitext(filename)[1].lower() in (".jpg", ".jpeg", ".heic"): + if os.path.splitext(filename)[1].lower() in EXIF_EXTENSIONS: exif = get_exif_data(img) except Exception as e: logger.warning("Could not read EXIF data from %s: %s", filename, e) diff --git a/src/sigal/settings.py b/src/sigal/settings.py index 2ed1b4f..c26be32 100644 --- a/src/sigal/settings.py +++ b/src/sigal/settings.py @@ -42,7 +42,16 @@ _DEFAULT_CONFIG = { "google_tag_manager": "", "ignore_directories": [], "ignore_files": [], - "img_extensions": [".jpg", ".jpeg", ".png", ".gif", ".tif", ".tiff", ".webp"], + "img_extensions": [ + ".jpg", + ".jpeg", + ".png", + ".gif", + ".heic", + ".tif", + ".tiff", + ".webp", + ], "img_processor": "ResizeToFit", "img_size": (640, 480), "img_format": None, diff --git a/src/sigal/templates/sigal.conf.py b/src/sigal/templates/sigal.conf.py index 77505a6..4c02597 100644 --- a/src/sigal/templates/sigal.conf.py +++ b/src/sigal/templates/sigal.conf.py @@ -66,7 +66,7 @@ img_size = (800, 600) # map_height = '500px' # File extensions that should be treated as images -# img_extensions = ['.jpg', '.jpeg', '.png', '.gif'] +# img_extensions = [".jpg", ".jpeg", ".png", ".gif", ".heic", ".tif", ".tiff", ".webp"] # Pilkit processor used to resize the image # (see http://pilkit.readthedocs.org/en/latest/#processors) diff --git a/tests/test_gallery.py b/tests/test_gallery.py index cf6978c..31489f0 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -301,7 +301,7 @@ def test_gallery(settings, tmp_path, caplog): gal = Gallery(settings, ncpu=1) gal.build() - assert re.match(r"CSS file .* could not be found", caplog.records[3].message) + assert re.match(r"CSS file .* could not be found", caplog.records[4].message) with open(tmp_path / "my.css", mode="w") as f: f.write("color: red") diff --git a/tests/test_image.py b/tests/test_image.py index 34298aa..fac4fc8 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -201,7 +201,9 @@ def test_get_exif_tags(): assert "datetime" not in simple assert "gps" not in simple + def test_get_heic_exif_tags(): + pytest.importorskip("pillow_heif") test_image = "outdoor.heic" src_file = os.path.join( CURRENT_DIR, "sample", "pictures", "dir1", "test1", test_image @@ -211,6 +213,7 @@ def test_get_heic_exif_tags(): assert simple["Make"] == "samsung" assert simple["datetime"] == "01/09/2024" + def test_get_iptc_data(caplog): test_image = "1.jpg" src_file = os.path.join(CURRENT_DIR, "sample", "pictures", "iptcTest", test_image)