Support iPhone MPO photos
This commit is contained in:
@@ -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
|
Metadata sidecars under `__MACOSX/` and `.DS_Store` are ignored. Ordinary API
|
||||||
clients can also send repeated `photos` parts; send either `archive` or `photos`,
|
clients can also send repeated `photos` parts; send either `archive` or `photos`,
|
||||||
never both.
|
never both.
|
||||||
Select JPEG, PNG, WebP, HEIC, AVIF, or TIFF still photos; videos/Live Photo video
|
Select JPEG/MPO, PNG, WebP, HEIC, AVIF, or TIFF still photos; videos/Live Photo
|
||||||
components aren't supported. The
|
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
|
service reads EXIF `DateTimeOriginal` automatically when it is present. Because
|
||||||
iOS Shortcuts can blank that EXIF field while creating the ZIP, `photo_dates`
|
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
|
supplies the same Photos-library metadata explicitly, one ISO 8601 value per
|
||||||
|
|||||||
+2
-2
@@ -128,9 +128,9 @@ def public_text(text):
|
|||||||
|
|
||||||
def web_image(raw, destination):
|
def web_image(raw, destination):
|
||||||
with Image.open(io.BytesIO(raw)) as original:
|
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(
|
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")
|
"HEIF/HEIC, AVIF, or TIFF photos")
|
||||||
taken_at = exif_photo_date(original.getexif())
|
taken_at = exif_photo_date(original.getexif())
|
||||||
image = ImageOps.exif_transpose(original)
|
image = ImageOps.exif_transpose(original)
|
||||||
|
|||||||
@@ -162,13 +162,18 @@ def test_tiff_and_invalid_color_profile_are_accepted(service):
|
|||||||
invalid_profile = io.BytesIO()
|
invalid_profile = io.BytesIO()
|
||||||
Image.new("RGB", (40, 20), "purple").save(
|
Image.new("RGB", (40, 20), "purple").save(
|
||||||
invalid_profile, "JPEG", icc_profile=b"not an ICC profile")
|
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])
|
response = upload(client, [raw])
|
||||||
assert response.status_code == 202
|
assert response.status_code == 202
|
||||||
output = data / "submissions" / response.json["id"] / "images/00.jpg"
|
output = data / "submissions" / response.json["id"] / "images/00.jpg"
|
||||||
with Image.open(output) as image:
|
with Image.open(output) as image:
|
||||||
assert image.format == "JPEG"
|
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):
|
def test_heic_format_label_is_accepted(service, monkeypatch):
|
||||||
|
|||||||
Reference in New Issue
Block a user