Default undated photo uploads to today

This commit is contained in:
2026-09-20 02:30:32 +03:30
parent cf46d62ede
commit b024474915
3 changed files with 10 additions and 8 deletions
+2 -2
View File
@@ -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 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; 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 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 photos. When none of these date sources is available, the service uses the upload
being placed under the upload year. Re-sharing the same images, caption, and date. Re-sharing the same images, caption, and
highlight with dates replaces the earlier undated upload. Captions are plain highlight with dates replaces the earlier undated upload. Captions are plain
text and optional. text and optional.
An optional highlight name (up to 200 characters) adds one link to **Highlights**, An optional highlight name (up to 200 characters) adds one link to **Highlights**,
+4 -4
View File
@@ -347,6 +347,7 @@ def create_app(data_dir=None, token=None):
if kind == "snippet": if kind == "snippet":
digest.update(b"\0snippet\0" + text.encode()) digest.update(b"\0snippet\0" + text.encode())
temporary = Path(tempfile.mkdtemp(dir=data / "temporary")) temporary = Path(tempfile.mkdtemp(dir=data / "temporary"))
received_at = datetime.now(timezone.utc)
try: try:
(temporary / "originals").mkdir() (temporary / "originals").mkdir()
(temporary / "images").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") embedded_date = web_image(raw, temporary / "images" / f"{index:02}.jpg")
if kind == "photo": if kind == "photo":
capture_dates.append(batch_date or 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, except (UnidentifiedImageError, OSError, ValueError, SyntaxError,
Image.DecompressionBombError, Image.DecompressionBombWarning, Image.DecompressionBombError, Image.DecompressionBombWarning,
ImageCms.PyCMSError): 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) normalized_dates = "\n".join(taken.isoformat() for taken in supplied_dates)
digest.update(b"\0photo_dates\0" + normalized_dates.encode()) digest.update(b"\0photo_dates\0" + normalized_dates.encode())
job = digest.hexdigest() job = digest.hexdigest()
date = datetime.now(timezone.utc).isoformat() date = received_at.isoformat()
manifest = {"id": job, "date": date, "caption": caption, manifest = {"id": job, "date": date, "caption": caption,
"highlight": highlight, "count": len(photos)} "highlight": highlight, "count": len(photos)}
if ((batch_date or supplied_dates) and undated_job != job and 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) manifest.update(kind=kind, text=text)
atomic_json(temporary / "manifest.json", manifest) atomic_json(temporary / "manifest.json", manifest)
if kind == "photo": 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) capture_dates = distinct_photo_dates(capture_dates)
manifest["capture_dates"] = [taken.isoformat() for taken in capture_dates] manifest["capture_dates"] = [taken.isoformat() for taken in capture_dates]
atomic_json(temporary / "manifest.json", manifest) atomic_json(temporary / "manifest.json", manifest)
+4 -2
View File
@@ -1,6 +1,7 @@
import io import io
import hashlib import hashlib
import json import json
from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import sys 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"] assert "valid ISO dates" in invalid.json["error"]
missing = upload(client, [photo(exif=Image.Exif())]) missing = upload(client, [photo(exif=Image.Exif())])
assert missing.status_code == 400 assert missing.status_code == 202
assert "capture date is missing" in missing.json["error"] 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): def test_batch_date_taken_overrides_metadata_for_analog_photos(service):