Browse Source

activate .heic by default and log warn if pillow_heif is missing

pull/519/head
Simon Conseil 1 year ago
parent
commit
8c8a6a2040
  1. 12
      src/sigal/gallery.py
  2. 20
      src/sigal/image.py
  3. 11
      src/sigal/settings.py
  4. 2
      src/sigal/templates/sigal.conf.py
  5. 2
      tests/test_gallery.py
  6. 3
      tests/test_image.py

12
src/sigal/gallery.py

@ -45,7 +45,13 @@ from natsort import natsort_keygen, ns
from PIL import Image as PILImage from PIL import Image as PILImage
from . import image, signals, video 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 .settings import Status, get_thumb
from .utils import ( from .utils import (
Devnull, Devnull,
@ -264,7 +270,7 @@ class Image(Media):
datetime_format = self.settings["datetime_format"] datetime_format = self.settings["datetime_format"]
return ( return (
get_exif_tags(self.raw_exif, datetime_format=datetime_format) 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 else None
) )
@ -289,7 +295,7 @@ class Image(Media):
@cached_property @cached_property
def raw_exif(self): def raw_exif(self):
"""If not `None`, contains the raw EXIF tags.""" """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"] return self.file_metadata["exif"]
@cached_property @cached_property

20
src/sigal/image.py

@ -45,15 +45,20 @@ from pilkit.processors import Transpose
from pilkit.utils import save_image from pilkit.utils import save_image
try: try:
from pillow_heif import HeifImagePlugin from pillow_heif import HeifImagePlugin # noqa: F401
except:
pass HAS_HEIF = True
except ImportError:
HAS_HEIF = False
from . import signals, utils from . import signals, utils
from .settings import Status
# Force loading of truncated files # Force loading of truncated files
ImageFile.LOAD_TRUNCATED_IMAGES = True ImageFile.LOAD_TRUNCATED_IMAGES = True
EXIF_EXTENSIONS = (".jpg", ".jpeg", ".heic")
def _has_exif_tags(img): def _has_exif_tags(img):
return hasattr(img, "info") and "exif" in img.info return hasattr(img, "info") and "exif" in img.info
@ -71,7 +76,7 @@ def _read_image(file_path):
for w in caught_warnings: for w in caught_warnings:
logger.warning( 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}" f"{w.category}: {w.message}"
) )
return im return im
@ -185,6 +190,11 @@ def process_image(media):
options = media.settings["jpg_options"] options = media.settings["jpg_options"]
elif media.src_ext == ".png": elif media.src_ext == ".png":
options = {"optimize": True} 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: else:
options = {} options = {}
@ -298,7 +308,7 @@ def get_image_metadata(filename):
logger.error("Could not open image %s metadata: %s", filename, e) logger.error("Could not open image %s metadata: %s", filename, e)
else: else:
try: 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) exif = get_exif_data(img)
except Exception as e: 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)

11
src/sigal/settings.py

@ -42,7 +42,16 @@ _DEFAULT_CONFIG = {
"google_tag_manager": "", "google_tag_manager": "",
"ignore_directories": [], "ignore_directories": [],
"ignore_files": [], "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_processor": "ResizeToFit",
"img_size": (640, 480), "img_size": (640, 480),
"img_format": None, "img_format": None,

2
src/sigal/templates/sigal.conf.py

@ -66,7 +66,7 @@ img_size = (800, 600)
# map_height = '500px' # map_height = '500px'
# File extensions that should be treated as images # 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 # Pilkit processor used to resize the image
# (see http://pilkit.readthedocs.org/en/latest/#processors) # (see http://pilkit.readthedocs.org/en/latest/#processors)

2
tests/test_gallery.py

@ -301,7 +301,7 @@ def test_gallery(settings, tmp_path, caplog):
gal = Gallery(settings, ncpu=1) gal = Gallery(settings, ncpu=1)
gal.build() 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: with open(tmp_path / "my.css", mode="w") as f:
f.write("color: red") f.write("color: red")

3
tests/test_image.py

@ -201,7 +201,9 @@ def test_get_exif_tags():
assert "datetime" not in simple assert "datetime" not in simple
assert "gps" not in simple assert "gps" not in simple
def test_get_heic_exif_tags(): def test_get_heic_exif_tags():
pytest.importorskip("pillow_heif")
test_image = "outdoor.heic" test_image = "outdoor.heic"
src_file = os.path.join( src_file = os.path.join(
CURRENT_DIR, "sample", "pictures", "dir1", "test1", test_image CURRENT_DIR, "sample", "pictures", "dir1", "test1", test_image
@ -211,6 +213,7 @@ def test_get_heic_exif_tags():
assert simple["Make"] == "samsung" assert simple["Make"] == "samsung"
assert simple["datetime"] == "01/09/2024" assert simple["datetime"] == "01/09/2024"
def test_get_iptc_data(caplog): def test_get_iptc_data(caplog):
test_image = "1.jpg" test_image = "1.jpg"
src_file = os.path.join(CURRENT_DIR, "sample", "pictures", "iptcTest", test_image) src_file = os.path.join(CURRENT_DIR, "sample", "pictures", "iptcTest", test_image)

Loading…
Cancel
Save