Update exif.py

This commit is contained in:
Nyymix 2024-11-20 15:07:15 +02:00
parent d103b31bf2
commit 6da82cb755

107
exif.py
View file

@ -1,5 +1,6 @@
import json import json
import logging import logging
import os
from datetime import datetime from datetime import datetime
import pytz import pytz
@ -7,7 +8,6 @@ from PIL import Image, IptcImagePlugin, TiffImagePlugin
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
""" Required exif values """ """ Required exif values """
TAGS_EXIF = { TAGS_EXIF = {
0x9003: "DateTimeOriginal", 0x9003: "DateTimeOriginal",
@ -34,62 +34,81 @@ TAGS_IPTC = {
class Exif: class Exif:
def __init__(self, filename: str): def __init__(self, filename: str):
self.filename = filename self.filename = filename
self._exif = {} self._exif = {}
self._iptc = {} self._iptc = {}
self.data = {} self.data = {}
""" Open image file """ self._process_image()
self._read_exif()
self._read_iptc()
def _process_image(self):
""" Open and verify image file """
try: try:
with Image.open(self.filename) as self.img: with Image.open(self.filename) as self.img:
self.img.verify() self.img.verify()
except Exception: except (OSError, IOError) as e:
logger.exception('Could not open image: %s', self.filename) 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: try:
self._exif = { exif_data = self.img._getexif()
TAGS_EXIF[k]: v if exif_data is not None:
for k, v in self.img._getexif().items() self._exif = {
if k in TAGS_EXIF TAGS_EXIF[k]: v
} for k, v in exif_data.items()
for (key, value) in self._exif.items(): if k in TAGS_EXIF
if isinstance(value, TiffImagePlugin.IFDRational): }
self.data[key] = float(value) for key, value in self._exif.items():
elif isinstance(value, tuple): if isinstance(value, TiffImagePlugin.IFDRational):
self.data[key] = tuple(float(t) if isinstance(t, TiffImagePlugin.IFDRational) else t for t in value) self.data[key] = float(value)
elif isinstance(value, bytes): elif isinstance(value, tuple):
self.data[key] = value.decode('utf-8') self.data[key] = tuple(float(t) if isinstance(t, TiffImagePlugin.IFDRational) else t for t in value)
else: elif isinstance(value, bytes):
self.data[key] = value self.data[key] = value.decode('utf-8')
except Exception: else:
logger.warning('Could not read exif metadata from: %s', self.filename) 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: try:
self._iptc = { iptc_data = IptcImagePlugin.getiptcinfo(self.img)
TAGS_IPTC[k]: v if iptc_data is not None:
for k, v in IptcImagePlugin.getiptcinfo(self.img).items() self._iptc = {
if k in TAGS_IPTC TAGS_IPTC[k]: v
} for k, v in iptc_data.items()
for (key, value) in self._iptc.items(): if k in TAGS_IPTC
if isinstance(value, list): }
self.data[key] = [x.decode('utf-8') for x in value] for key, value in self._iptc.items():
else: if isinstance(value, list):
self.data[key] = value.decode('utf-8') self.data[key] = [x.decode('utf-8') for x in value]
except Exception: else:
logger.warning('Could not read iptc metadata from: %s', self.filename) 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'): 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: if 'DateTimeOriginal' in self.data:
try: try:
return datetime.strptime(self.data['DateTimeOriginal'], "%Y:%m:%d %H:%M:%S").astimezone(pytz.timezone(timezone)) return datetime.strptime(self.data['DateTimeOriginal'], "%Y:%m:%d %H:%M:%S").astimezone(pytz.timezone(timezone))
except ValueError as e: except ValueError:
logger.warning('Could not parse date from: %s', self.filename) logger.warning('Invalid EXIF DateTimeOriginal format in: %s', self.filename)
else:
return datetime.now(pytz.timezone(timezone)) # 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): def json(self):
""" Return exif data in json format """ """ Return exif data in json format """
@ -97,11 +116,7 @@ class Exif:
def keywords(self): def keywords(self):
""" Return Keywords list """ """ Return Keywords list """
if 'Keywords' not in self.data: return self.data.get('Keywords', [])
return []
if type(self.data['Keywords']) == list:
return self.data['Keywords']
return [self.data['Keywords']]
def __str__(self): def __str__(self):
return self.filename return self.filename