From 6da82cb7556030eddd0c4a31f9989633b3ff5bba Mon Sep 17 00:00:00 2001 From: nyymix Date: Wed, 20 Nov 2024 15:07:15 +0200 Subject: [PATCH] Update exif.py --- exif.py | 107 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/exif.py b/exif.py index 356685d..d4e86c6 100644 --- a/exif.py +++ b/exif.py @@ -1,5 +1,6 @@ import json import logging +import os from datetime import datetime import pytz @@ -7,7 +8,6 @@ from PIL import Image, IptcImagePlugin, TiffImagePlugin logger = logging.getLogger(__name__) - """ Required exif values """ TAGS_EXIF = { 0x9003: "DateTimeOriginal", @@ -34,62 +34,81 @@ TAGS_IPTC = { class Exif: def __init__(self, filename: str): - self.filename = filename self._exif = {} self._iptc = {} self.data = {} - """ Open image file """ + self._process_image() + self._read_exif() + self._read_iptc() + + def _process_image(self): + """ Open and verify image file """ try: with Image.open(self.filename) as self.img: self.img.verify() - except Exception: - logger.exception('Could not open image: %s', self.filename) + except (OSError, IOError) as e: + logger.exception('Could not open or verify image: %s', self.filename) + raise e - """ Read exif tags """ + def _read_exif(self): + """ Read and process EXIF metadata """ try: - self._exif = { - TAGS_EXIF[k]: v - for k, v in self.img._getexif().items() - if k in TAGS_EXIF - } - for (key, value) in self._exif.items(): - if isinstance(value, TiffImagePlugin.IFDRational): - self.data[key] = float(value) - elif isinstance(value, tuple): - self.data[key] = tuple(float(t) if isinstance(t, TiffImagePlugin.IFDRational) else t for t in value) - elif isinstance(value, bytes): - self.data[key] = value.decode('utf-8') - else: - self.data[key] = value - except Exception: - logger.warning('Could not read exif metadata from: %s', self.filename) + exif_data = self.img._getexif() + if exif_data is not None: + self._exif = { + TAGS_EXIF[k]: v + for k, v in exif_data.items() + if k in TAGS_EXIF + } + for key, value in self._exif.items(): + if isinstance(value, TiffImagePlugin.IFDRational): + self.data[key] = float(value) + elif isinstance(value, tuple): + self.data[key] = tuple(float(t) if isinstance(t, TiffImagePlugin.IFDRational) else t for t in value) + elif isinstance(value, bytes): + self.data[key] = value.decode('utf-8') + else: + self.data[key] = value + except AttributeError: + logger.warning('No EXIF metadata found for: %s', self.filename) + except Exception as e: + logger.warning('Could not read EXIF metadata from: %s, Error: %s', self.filename, e) - """ Read iptc tags """ + def _read_iptc(self): + """ Read and process IPTC metadata """ try: - self._iptc = { - TAGS_IPTC[k]: v - for k, v in IptcImagePlugin.getiptcinfo(self.img).items() - if k in TAGS_IPTC - } - for (key, value) in self._iptc.items(): - if isinstance(value, list): - self.data[key] = [x.decode('utf-8') for x in value] - else: - self.data[key] = value.decode('utf-8') - except Exception: - logger.warning('Could not read iptc metadata from: %s', self.filename) + iptc_data = IptcImagePlugin.getiptcinfo(self.img) + if iptc_data is not None: + self._iptc = { + TAGS_IPTC[k]: v + for k, v in iptc_data.items() + if k in TAGS_IPTC + } + for key, value in self._iptc.items(): + if isinstance(value, list): + self.data[key] = [x.decode('utf-8') for x in value] + else: + self.data[key] = value.decode('utf-8') + except Exception as e: + logger.warning('Could not read IPTC metadata from: %s, Error: %s', self.filename, e) def datetimeoriginal(self, timezone='UTC'): - """ Return DateTimeOriginal with timezone """ + """ Return DateTimeOriginal with timezone, or fallback to file creation date if not available. """ if 'DateTimeOriginal' in self.data: try: return datetime.strptime(self.data['DateTimeOriginal'], "%Y:%m:%d %H:%M:%S").astimezone(pytz.timezone(timezone)) - except ValueError as e: - logger.warning('Could not parse date from: %s', self.filename) - else: - return datetime.now(pytz.timezone(timezone)) + except ValueError: + logger.warning('Invalid EXIF DateTimeOriginal format in: %s', self.filename) + + # If EXIF DateTimeOriginal is not available, fallback to file creation date + try: + creation_time = os.path.getctime(self.filename) + return datetime.fromtimestamp(creation_time).astimezone(pytz.timezone(timezone)) + except Exception as e: + logger.warning('Could not retrieve file creation date for: %s, Error: %s', self.filename, e) + return datetime.now(pytz.timezone(timezone)) # Fallback to current time if all else fails def json(self): """ Return exif data in json format """ @@ -97,11 +116,7 @@ class Exif: def keywords(self): """ Return Keywords list """ - if 'Keywords' not in self.data: - return [] - if type(self.data['Keywords']) == list: - return self.data['Keywords'] - return [self.data['Keywords']] + return self.data.get('Keywords', []) def __str__(self): - return self.filename + return self.filename \ No newline at end of file