diff --git a/elodie/media/photo.py b/elodie/media/photo.py index 1121276..b070877 100644 --- a/elodie/media/photo.py +++ b/elodie/media/photo.py @@ -12,10 +12,8 @@ import os import re import time from datetime import datetime -from PIL import Image from re import compile - from elodie import log from .media import Media @@ -38,6 +36,15 @@ class Photo(Media): # We only want to parse EXIF once so we store it here self.exif = None + # Optionally import Pillow - see gh-325 + # https://github.com/jmathai/elodie/issues/325 + self.pillow = None + try: + from PIL import Image + self.pillow = Image + except ImportError: + pass + def get_date_taken(self): """Get the date which the photo was taken. @@ -98,22 +105,28 @@ class Photo(Media): if(extension != 'heic'): # gh-4 This checks if the source file is an image. # It doesn't validate against the list of supported types. + # We check with imghdr and pillow. if(imghdr.what(source) is None): - # imghdr won't detect all variants of images (https://bugs.python.org/issue28591) - # see https://github.com/jmathai/elodie/issues/281 - # before giving up, we use `pillow` imaging library to detect file type - # - # It is important to note that the library doesn't decode or load the - # raster data unless it really has to. When you open a file, - # the file header is read to determine the file format and extract - # things like mode, size, and other properties required to decode the file, - # but the rest of the file is not processed until later. - try: - im = Image.open(source) - except IOError: - return False - - if(im.format is None): + # Pillow is used as a fallback and if it's not available we trust + # what imghdr returned. + if(self.pillow is None): return False + else: + # imghdr won't detect all variants of images (https://bugs.python.org/issue28591) + # see https://github.com/jmathai/elodie/issues/281 + # before giving up, we use `pillow` imaging library to detect file type + # + # It is important to note that the library doesn't decode or load the + # raster data unless it really has to. When you open a file, + # the file header is read to determine the file format and extract + # things like mode, size, and other properties required to decode the file, + # but the rest of the file is not processed until later. + try: + im = self.pillow.open(source) + except IOError: + return False + if(im.format is None): + return False + return extension in self.extensions diff --git a/elodie/tests/media/photo_test.py b/elodie/tests/media/photo_test.py index d299cfa..110bed6 100644 --- a/elodie/tests/media/photo_test.py +++ b/elodie/tests/media/photo_test.py @@ -165,6 +165,11 @@ def test_is_valid_fallback_using_pillow(): assert photo.is_valid() +def test_pillow_not_loaded(): + photo = Photo(helper.get_file('imghdr-error.jpg')) + photo.pillow = None + + assert photo.is_valid() == False def test_set_album(): temporary_folder, folder = helper.create_working_folder()