From a517d774c2735c800ed774d9aa91685dfcbf8636 Mon Sep 17 00:00:00 2001 From: HERITAGE-XION Date: Sun, 4 Jan 2026 21:13:37 +0100 Subject: [PATCH] metadata-scrubber-tool: added the png_metadata processing functionalities, added an exception class to handle various error --- src/__pycache__/main.cpython-314.pyc | Bin 1701 -> 0 bytes src/services/image_handler.py | 7 ++- src/utils/exceptions.py | 22 +++++++ src/utils/jpeg_metadata.py | 89 +++++++++++++++------------ src/utils/png_metadata.py | 60 ++++++++++++++++++ 5 files changed, 137 insertions(+), 41 deletions(-) delete mode 100644 src/__pycache__/main.cpython-314.pyc create mode 100644 src/utils/exceptions.py create mode 100644 src/utils/png_metadata.py diff --git a/src/__pycache__/main.cpython-314.pyc b/src/__pycache__/main.cpython-314.pyc deleted file mode 100644 index b26d92cb358627fd7aa5227d0adeed0f42283206..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1701 zcmah}%WoS+7@vK4ZFlQDC~8VlGD+Hm6|sXL&X8X+>u8BWSF-$^6Gh&+QXZ-SYQ;PMAfgc4f0an|ved1Omu<*$TuL;x%b>9NgLV0vkUiub% zgKh$SAyp+yDuAUoEV&>L_yu_enaVRR$Et68Bu?B4sAs#T94ir{v04rS7cT9oS;TLg zjz7Hke&Jd~XjHgSCA3y>$XCP-LXY^Y;1Om!HnX!)h1SbuLbD896e3zFcy`Utg^k!K zm2BS+n9WG3L{Eat6ELIKfwa(bG$gG^`-1K&9kKp$wI_FB2dW%K4?+?qWT|$Wz(D5G z9ISlw>S{@OWB@YWRzlfQLS-#!sY0U(O(Il|>axKr_@7bG28z6F>3PLUUg+;y?AIQ; zm4X_ko1dkx*bQ7It{Y$$U>nz=K)LklEu78vYKm(SS32{!#_$#;ghkj3D8XUi));Xc zxpZ@KIS3r=fG6^N+V*#0~Bbi##|C>5o@?) z-Be<|(nDgbg|y}~szQZKmFloi-i=uINEAW1u~rG(fJS`7>@3FA;8smkh>iiz-(_?e z2sGV5$;^{XcF)<%x8J$eHu4<>rG`5RG&1(R_T<#`{l)vU?KeJYkAAwN|1mtaqa7rW zW^AwBS=&ATv)-Ot`dxc4dg-CMOf%r{*yG@E;Z5tF6IZ?baV)^oGi929ts*5;qAB2E z5fByd7l{bi3c_IF|Du*F4+ni{5ud z$H8!x8_xh~p@Ve4&Ofg69_$(I(RUxJ?}^0XWU0j8bg5)YmrMh&(g+ES4O**IbA6al zUivsKR3ZPk1YlzAs@SU7VJO1lu<|_HcLXv-5ZpWel1ioDkhrcFXz~LuaOy7kh@J<} zydjYbq@zfZ^d~YNBm5Xm0C03;`}Q}t?|LoeQF`Q_+0sELYun>@#_yi}N9KP_S3&Yn lN3fl9hqeP5+SfM$*U2E&*l9jc&OB1}?>^sGkoua4=3n$eXo~;< diff --git a/src/services/image_handler.py b/src/services/image_handler.py index 7d588d4c..e09435f6 100644 --- a/src/services/image_handler.py +++ b/src/services/image_handler.py @@ -7,12 +7,17 @@ from PIL import Image from src.services.metadata_handler import MetadataHandler from src.utils.jpeg_metadata import JpegProcessaor +from src.utils.png_metadata import PngProcessor class ImageHandler(MetadataHandler): def __init__(self, filepath: str): super().__init__(filepath) - self.processors = {".jpeg": JpegProcessaor(), ".jpg": JpegProcessaor()} + self.processors = { + ".jpeg": JpegProcessaor(), + ".jpg": JpegProcessaor(), + ".png": PngProcessor(), + } self.tags_to_delete = [] def read(self): diff --git a/src/utils/exceptions.py b/src/utils/exceptions.py new file mode 100644 index 00000000..90bb1c43 --- /dev/null +++ b/src/utils/exceptions.py @@ -0,0 +1,22 @@ +class MetadataException(Exception): + """Base class for other exceptions in this module.""" + + pass + + +class UnsupportedFormatError(MetadataException): + """Exception raised when an unsupported file format is encountered.""" + + pass + + +class MetadataNotFoundError(MetadataException): + """Exception raised when metadata is not found.""" + + pass + + +class MetadataProcessingError(MetadataException): + """Exception raised when an error occurs during metadata processing.""" + + pass diff --git a/src/utils/jpeg_metadata.py b/src/utils/jpeg_metadata.py index 8428c1f4..ed005c2d 100644 --- a/src/utils/jpeg_metadata.py +++ b/src/utils/jpeg_metadata.py @@ -1,4 +1,6 @@ -import piexif +import piexif # pyright: ignore[reportMissingTypeStubs] + +from src.utils.exceptions import MetadataNotFoundError, MetadataProcessingError class JpegProcessaor: @@ -7,48 +9,55 @@ class JpegProcessaor: self.data = {} def get_metadata(self, img): - if "exif" in img.info: + try: + if "exif" in img.info: + exif_dict = piexif.load(img.info["exif"]) + for ifd, value in exif_dict.items(): + # this exclude thumbnail IFD (its the thumbnails blob data. it can be removed but the image will take a couple more seconds to load) + if not isinstance(exif_dict[ifd], dict): + continue + + # iterate through the IFD + for tag, tag_value in exif_dict[ifd].items(): + tag_info = piexif.TAGS[ifd].get(tag, {}) + + # Get the human-readable name for the tag + tag_name = ( + tag_info.get("name", "Unknown Tag") + if tag_info + else "Unknown Tag" + ) + + # exculudes tags that are necessary for image display integrity + if ( + tag_name == "Orientation" + or tag_name == "ColorSpace" + or tag_name == "ExifTag" + ): + continue + + # save to list and dict + self.tags_to_delete.append(tag) + self.data[tag_name] = tag_value + return {"data": self.data, "tags_to_delete": self.tags_to_delete} + else: + raise MetadataNotFoundError("No EXIF data found in the image.") + except MetadataNotFoundError as e: + return f"Error: {str(e)}" + + def delete_metadata(self, img, tags_to_delete): + try: exif_dict = piexif.load(img.info["exif"]) for ifd, value in exif_dict.items(): - # this exclude thumbnail IFD (its the thumbnails blob data. it can be removed but the image will take a couple more seconds to load) + # exclude thumbnail IFD (its the thumbnails blob data so i dont wanna deal with that) if not isinstance(exif_dict[ifd], dict): continue - # iterate through the IFD - for tag, tag_value in exif_dict[ifd].items(): - tag_info = piexif.TAGS[ifd].get(tag, {}) + # iterate through and delete tags + for tag in list(exif_dict[ifd]): + if tag in tags_to_delete: + del exif_dict[ifd][tag] - # Get the human-readable name for the tag - tag_name = ( - tag_info.get("name", "Unknown Tag") - if tag_info - else "Unknown Tag" - ) - - # exculudes tags that are necessary for image display integrity - if ( - tag_name == "Orientation" - or tag_name == "ColorSpace" - or tag_name == "ExifTag" - ): - continue - - # save to list and dict - self.tags_to_delete.append(tag) - self.data[tag_name] = tag_value - - return {"data": self.data, "tags_to_delete": self.tags_to_delete} - - def delete_metadata(self, img, tags_to_delete): - exif_dict = piexif.load(img.info["exif"]) - for ifd, value in exif_dict.items(): - # exclude thumbnail IFD (its the thumbnails blob data so i dont wanna deal with that) - if not isinstance(exif_dict[ifd], dict): - continue - - # iterate through and delete tags - for tag in list(exif_dict[ifd]): - if tag in tags_to_delete: - del exif_dict[ifd][tag] - - return exif_dict + return exif_dict + except Exception as e: + raise MetadataProcessingError(f"Error Processing: {str(e)}") diff --git a/src/utils/png_metadata.py b/src/utils/png_metadata.py new file mode 100644 index 00000000..1d88e0b3 --- /dev/null +++ b/src/utils/png_metadata.py @@ -0,0 +1,60 @@ +from PIL import ExifTags + +from src.utils.exceptions import MetadataNotFoundError, MetadataProcessingError + + +class PngProcessor: + def __init__(self): + self.tags_to_delete = [] + self.data = {} + + def get_metadata(self, img): + img.load() + exif = img.getexif() + try: + if exif: + # iterate through the (0th) IFD + for tag, value in exif.items(): + # Get the human-readable name for the tag + tag_name = ExifTags.TAGS.get(tag, tag) + + # save to list and dict + self.tags_to_delete.append(tag) + self.data[tag_name] = value + print(f"{tag_name}: {value}") + + # terate through the (GPS) IFD + gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) + for tag, value in gps_ifd.items(): + # Get the human-readable name for the tag + tag_name = ExifTags.GPSTAGS.get(tag, tag) + + # save to list and dict + self.tags_to_delete.append(tag) + self.data[tag_name] = value + print(f"{tag_name}: {value}") + + return {"data": self.data, "tags_to_delete": self.tags_to_delete} + else: + raise MetadataNotFoundError("No EXIF data found in the image.") + except MetadataNotFoundError as e: + return f"Error: {str(e)}" + + def delete_metadata(self, img, tags_to_delete): + img.load() + exif = img.getexif() + try: + # iterate through the (0th) IFD + for tag_id, value in exif.items(): + if tag_id in tags_to_delete: + del exif[tag_id] + + # terate through the (GPS) IFD + gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) + for tag_id, value in gps_ifd.items(): + if tag_id in tags_to_delete: + del gps_ifd[tag_id] + + return exif + gps_ifd + except Exception as e: + raise MetadataProcessingError(f"Error Processing image: {str(e)}")