diff --git a/backend/app/routers/photos.py b/backend/app/routers/photos.py index b86adc2..9c76500 100644 --- a/backend/app/routers/photos.py +++ b/backend/app/routers/photos.py @@ -745,8 +745,16 @@ def _generate_proxy_webp(src_path: str, dst_path: str) -> None: from io import BytesIO img = Image.open(BytesIO(thumb.data)) except Exception as e2: - logger.error(f"RAW preview extraction also failed for {src_path}: {e2}") - raise HTTPException(status_code=415, detail="Unable to decode RAW file") + # Last-resort: iPhone "Apple ProRAW" / Linear DNG isn't a + # Bayer-pattern RAW — LibRaw rejects it. The file IS a TIFF + # container with a developed RGB image inside, so PIL opens + # it directly. Same fallback covers misnamed TIFFs. + logger.warning(f"RAW preview extraction failed for {src_path}: {e2}; trying PIL TIFF fallback") + try: + img = Image.open(src_path) + except Exception as e3: + logger.error(f"PIL fallback also failed for {src_path}: {e3}") + raise HTTPException(status_code=415, detail="Unable to decode RAW file") # HEIC/HEIF — pillow-heif registers a PIL plugin elif ext in {'.heic', '.heif'}: diff --git a/backend/app/tasks/thumbs.py b/backend/app/tasks/thumbs.py index cd70e34..f466d6f 100644 --- a/backend/app/tasks/thumbs.py +++ b/backend/app/tasks/thumbs.py @@ -74,9 +74,19 @@ def process_raw_image(filepath: str) -> Image.Image: # Convert numpy array to PIL Image return Image.fromarray(rgb, 'RGB') except Exception as e: - logger.error(f"Error processing RAW file {filepath}: {e}") - # Try to extract embedded JPEG preview - return extract_raw_preview(filepath) + logger.warning(f"rawpy failed for {filepath}: {e}; trying embedded preview") + preview = extract_raw_preview(filepath) + if preview is not None: + return preview + # iPhone "Apple ProRAW" / Linear DNG has no embedded preview and + # LibRaw rejects it as not-a-RAW. It IS a TIFF container with a + # developed RGB image inside, so PIL opens it directly. + try: + logger.warning(f"embedded preview missing for {filepath}; trying PIL TIFF fallback") + return Image.open(filepath) + except Exception as e2: + logger.error(f"PIL fallback also failed for {filepath}: {e2}") + raise else: # Use exiftool to extract embedded preview return extract_raw_preview(filepath)