diff --git a/photo-upload/README.md b/photo-upload/README.md index 30ff449..8459509 100644 --- a/photo-upload/README.md +++ b/photo-upload/README.md @@ -173,8 +173,8 @@ 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 image in archive order. `date_taken` overrides both sources for the whole batch; it accepts a year, calendar date, or ISO timestamp and is intended for analog -photos. An upload without any date source is rejected instead of -being placed under the upload year. Re-sharing the same images, caption, and +photos. When none of these date sources is available, the service uses the upload +date. Re-sharing the same images, caption, and highlight with dates replaces the earlier undated upload. Captions are plain text and optional. An optional highlight name (up to 200 characters) adds one link to **Highlights**, diff --git a/photo-upload/app.py b/photo-upload/app.py index ce638d8..b0835ed 100644 --- a/photo-upload/app.py +++ b/photo-upload/app.py @@ -347,6 +347,7 @@ def create_app(data_dir=None, token=None): if kind == "snippet": digest.update(b"\0snippet\0" + text.encode()) temporary = Path(tempfile.mkdtemp(dir=data / "temporary")) + received_at = datetime.now(timezone.utc) try: (temporary / "originals").mkdir() (temporary / "images").mkdir() @@ -359,7 +360,8 @@ def create_app(data_dir=None, token=None): embedded_date = web_image(raw, temporary / "images" / f"{index:02}.jpg") if kind == "photo": capture_dates.append(batch_date or - (supplied_dates[index] if supplied_dates else embedded_date)) + (supplied_dates[index] if supplied_dates else embedded_date) or + received_at) except (UnidentifiedImageError, OSError, ValueError, SyntaxError, Image.DecompressionBombError, Image.DecompressionBombWarning, ImageCms.PyCMSError): @@ -374,7 +376,7 @@ def create_app(data_dir=None, token=None): normalized_dates = "\n".join(taken.isoformat() for taken in supplied_dates) digest.update(b"\0photo_dates\0" + normalized_dates.encode()) job = digest.hexdigest() - date = datetime.now(timezone.utc).isoformat() + date = received_at.isoformat() manifest = {"id": job, "date": date, "caption": caption, "highlight": highlight, "count": len(photos)} if ((batch_date or supplied_dates) and undated_job != job and @@ -384,8 +386,6 @@ def create_app(data_dir=None, token=None): manifest.update(kind=kind, text=text) atomic_json(temporary / "manifest.json", manifest) if kind == "photo": - if any(taken is None for taken in capture_dates): - abort(400, "Photo capture date is missing; send date_taken or photo_dates") capture_dates = distinct_photo_dates(capture_dates) manifest["capture_dates"] = [taken.isoformat() for taken in capture_dates] atomic_json(temporary / "manifest.json", manifest) diff --git a/photo-upload/tests/test_upload.py b/photo-upload/tests/test_upload.py index aa26500..0ae6ed2 100644 --- a/photo-upload/tests/test_upload.py +++ b/photo-upload/tests/test_upload.py @@ -1,6 +1,7 @@ import io import hashlib import json +from datetime import datetime, timezone from pathlib import Path import subprocess import sys @@ -191,8 +192,9 @@ def test_photo_dates_can_come_from_photos_library_metadata(service): assert "valid ISO dates" in invalid.json["error"] missing = upload(client, [photo(exif=Image.Exif())]) - assert missing.status_code == 400 - assert "capture date is missing" in missing.json["error"] + assert missing.status_code == 202 + manifest = json.loads((data / "submissions" / missing.json["id"] / "manifest.json").read_text()) + assert datetime.fromisoformat(manifest["capture_dates"][0]).date() == datetime.now(timezone.utc).date() def test_batch_date_taken_overrides_metadata_for_analog_photos(service):