| """ |
| EXIF metadata provider. |
| |
| Delegates all parsing to cores.metadata.extractor — no duplicated |
| Pillow open / EXIF tag walk / GPS conversion logic. |
| """ |
|
|
| from __future__ import annotations |
|
|
| from config.settings import Settings, settings as _default_settings |
| from cores.metadata import extract_all |
| from pipeline.feature_extraction import PipelineOutput |
| from providers.base import BaseProvider, ProviderCapability |
|
|
|
|
| class EXIFProvider(BaseProvider): |
| name = "exif" |
| capability = ProviderCapability.METADATA |
|
|
| def __init__(self, settings: Settings | None = None) -> None: |
| super().__init__(settings=settings or _default_settings) |
|
|
| def is_available(self) -> bool: |
| try: |
| import PIL |
| return True |
| except ImportError: |
| return False |
|
|
| def _run(self, pipeline_output: PipelineOutput) -> tuple[dict, dict]: |
| original = pipeline_output.original_bytes |
| if not original: |
| raw = {"error": "No original bytes available for EXIF extraction"} |
| normalized = { |
| "format": pipeline_output.original_format, |
| "exif": {}, "xmp": {}, "iptc": {}, |
| } |
| return raw, normalized |
|
|
| data = extract_all(original) |
|
|
| raw = { |
| "format": data["format"], |
| "exif_tags_count": len(data["exif"]), |
| "gps_tags_count": len(data["gps"]), |
| "exif": data["exif"], |
| "gps": data["gps"], |
| "camera_make": data["camera_make"], |
| "camera_model": data["camera_model"], |
| "software": data["software"], |
| "capture_time": data["capture_time"], |
| "error": data["error"], |
| } |
| normalized = { |
| "format": data["format"], |
| "exif": data["exif"], |
| "xmp": {} if not data["xmp"] else {"raw_length": len(data["xmp"])}, |
| "iptc": data["iptc"], |
| "gps": data["gps_coords"], |
| "camera_make": data["camera_make"], |
| "camera_model": data["camera_model"], |
| "software": data["software"], |
| "capture_time": data["capture_time"], |
| } |
| return raw, normalized |
|
|