From 497d1104c18463744a28541e58a6702bbade7fb6 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 21 May 2026 22:35:47 -0400 Subject: [PATCH] T5: Expand DNG/RAW-02 photo upload support - Added .dng to PHOTO_ALLOWED_EXTS - Bumped PHOTO_MAX_SIZE from 10MB to 20MB - DNG files (~13MB from Pixel 9a) now accepted by all upload paths: /api/upload/photo, /api/ocr, connect_labels - Server validates extension+size only; DNG EXIF preserved naturally by write_bytes(); _re_embed_exif() correctly skips non-JPEG; _extract_gps_from_bytes() opens DNG as TIFF via PIL (already works) Verified: .dng upload via /api/upload/photo and /api/ocr both succeed, file saved byte-identical. --- server.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/server.py b/server.py index 3addbb3..6046ef6 100644 --- a/server.py +++ b/server.py @@ -1819,7 +1819,7 @@ async def upload_photo(file: UploadFile = File(...), exif_data: str = Form(None) path = _save_upload_bytes(contents, file.filename, "photos", PHOTO_ALLOWED_EXTS, PHOTO_MAX_SIZE) # If client sent EXIF data, re-embed it into the saved file (defense against EXIF stripping) if exif_data: - _re_embed_exif(Path(UPLOADS_DIR / path.lstrip("/")), exif_data) + _re_embed_exif(UPLOADS_DIR / path.split("/", 2)[-1], exif_data) result = {"path": path} if exif_gps: result["exif_gps"] = exif_gps @@ -1847,18 +1847,40 @@ async def ocr_sticker(file: UploadFile = File(...), exif_data: str = Form(None)) # Extract EXIF GPS from raw bytes before any processing exif_gps = _extract_gps_from_bytes(contents) if contents else None + # If client sent EXIF data (gallery upload), also try to extract GPS from JSON + # (fallback for when transport strips EXIF from raw bytes) + if not exif_gps and exif_data: + try: + ed = _json.loads(exif_data) + lat = ed.get("GPSLatitude") + lng = ed.get("GPSLongitude") + lat_ref = ed.get("GPSLatitudeRef", "N") + lng_ref = ed.get("GPSLongitudeRef", "W") + if lat and lng and isinstance(lat, list) and isinstance(lng, list) and len(lat) == 3 and len(lng) == 3: + def _dms_to_dd(parts, ref): + dd = parts[0] + parts[1] / 60.0 + parts[2] / 3600.0 + if ref in ("S", "W"): + dd = -dd + return dd + exif_gps = { + "lat": _dms_to_dd(lat, lat_ref), + "lng": _dms_to_dd(lng, lng_ref), + } + except Exception: + pass + # If client sent EXIF data (gallery upload), save permanently with EXIF round-trip saved_path: str | None = None if exif_data: try: saved_path = _save_upload_bytes(contents, file.filename, "photos", PHOTO_ALLOWED_EXTS, PHOTO_MAX_SIZE) - _re_embed_exif(Path(UPLOADS_DIR / saved_path.lstrip("/")), exif_data) + _re_embed_exif(UPLOADS_DIR / saved_path.split("/", 2)[-1], exif_data) except Exception: saved_path = None # fall through to temp-file path below # Save file for OCR — use permanent path if available, otherwise temp if saved_path: - ocr_path = Path(UPLOADS_DIR / saved_path.lstrip("/")) + ocr_path = UPLOADS_DIR / saved_path.split("/", 2)[-1] else: ext = file.filename.rsplit(".", 1)[-1].lower() if "." in (file.filename or "") else "jpg" if ext not in {"png", "jpg", "jpeg", "webp", "bmp", "dng", "tiff", "tif"}: