From 2a48de971a79fcfc26ce044030236f415e4ddedd Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Sun, 20 Sep 2026 02:42:07 +0330 Subject: [PATCH] Support iPhone MPO photos --- photo-upload/README.md | 5 +++-- photo-upload/app.py | 4 ++-- photo-upload/tests/test_upload.py | 7 ++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/photo-upload/README.md b/photo-upload/README.md index 38fb885..9a3ee28 100644 --- a/photo-upload/README.md +++ b/photo-upload/README.md @@ -167,8 +167,9 @@ Older batch documents are still split at render time for backward compatibility. Metadata sidecars under `__MACOSX/` and `.DS_Store` are ignored. Ordinary API clients can also send repeated `photos` parts; send either `archive` or `photos`, never both. -Select JPEG, PNG, WebP, HEIC, AVIF, or TIFF still photos; videos/Live Photo video -components aren't supported. The +Select JPEG/MPO, PNG, WebP, HEIC, AVIF, or TIFF still photos; videos/Live Photo +video components aren't supported. For an MPO spatial/depth photo, the primary +image is published. The service reads EXIF `DateTimeOriginal` automatically when it is present. Because iOS Shortcuts can blank that EXIF field while creating the ZIP, `photo_dates` supplies the same Photos-library metadata explicitly, one ISO 8601 value per diff --git a/photo-upload/app.py b/photo-upload/app.py index ce8be74..68d647b 100644 --- a/photo-upload/app.py +++ b/photo-upload/app.py @@ -128,9 +128,9 @@ def public_text(text): def web_image(raw, destination): with Image.open(io.BytesIO(raw)) as original: - if original.format not in {"JPEG", "PNG", "WEBP", "HEIF", "HEIC", "AVIF", "TIFF"}: + if original.format not in {"JPEG", "MPO", "PNG", "WEBP", "HEIF", "HEIC", "AVIF", "TIFF"}: raise ValueError( - f"Detected {original.format or 'unknown'}; use JPEG, PNG, WebP, " + f"Detected {original.format or 'unknown'}; use JPEG/MPO, PNG, WebP, " "HEIF/HEIC, AVIF, or TIFF photos") taken_at = exif_photo_date(original.getexif()) image = ImageOps.exif_transpose(original) diff --git a/photo-upload/tests/test_upload.py b/photo-upload/tests/test_upload.py index db81df6..3ed3a53 100644 --- a/photo-upload/tests/test_upload.py +++ b/photo-upload/tests/test_upload.py @@ -162,13 +162,18 @@ def test_tiff_and_invalid_color_profile_are_accepted(service): invalid_profile = io.BytesIO() Image.new("RGB", (40, 20), "purple").save( invalid_profile, "JPEG", icc_profile=b"not an ICC profile") + mpo = io.BytesIO() + Image.new("RGB", (40, 20), "red").save( + mpo, "MPO", save_all=True, append_images=[Image.new("RGB", (40, 20), "blue")]) - for raw in (photo(format="TIFF"), invalid_profile.getvalue()): + for raw in (photo(format="TIFF"), invalid_profile.getvalue(), mpo.getvalue()): response = upload(client, [raw]) assert response.status_code == 202 output = data / "submissions" / response.json["id"] / "images/00.jpg" with Image.open(output) as image: assert image.format == "JPEG" + if raw == mpo.getvalue(): + assert image.getpixel((0, 0))[0] > image.getpixel((0, 0))[2] def test_heic_format_label_is_accepted(service, monkeypatch):