fix: ffmpeg fallback for HEIC files that libheif rejects
iPhone photos with depth maps or gain maps have too many auxiliary image references for libheif 1.17, causing pillow-heif to throw "Too many auxiliary image references". process_heic_image() now falls back to ffmpeg when pillow-heif fails — ffmpeg's own HEIC decoder handles these files without issue. Fixes 27/35 HEIC photos that were stuck in failed state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,17 +102,36 @@ def extract_raw_preview(filepath: str) -> Optional[Image.Image]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def process_heic_image(filepath: str) -> Image.Image:
|
def process_heic_image(filepath: str) -> Image.Image:
|
||||||
"""Process HEIC/HEIF image formats"""
|
"""Process HEIC/HEIF image formats.
|
||||||
|
|
||||||
|
Tries pillow-heif first (fast, native). Falls back to ffmpeg for
|
||||||
|
files that libheif rejects — e.g. iPhone photos with too many
|
||||||
|
auxiliary image references (depth maps, gain maps).
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
# Use pillow-heif to open the image
|
|
||||||
img = Image.open(filepath)
|
img = Image.open(filepath)
|
||||||
# Convert to RGB if needed
|
|
||||||
if img.mode != 'RGB':
|
if img.mode != 'RGB':
|
||||||
img = img.convert('RGB')
|
img = img.convert('RGB')
|
||||||
return img
|
return img
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing HEIC file {filepath}: {e}")
|
logger.warning(f"pillow-heif failed for {filepath}: {e} — trying ffmpeg")
|
||||||
raise
|
|
||||||
|
# ffmpeg fallback: decode HEIC to PNG in memory.
|
||||||
|
import subprocess
|
||||||
|
from io import BytesIO
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['ffmpeg', '-i', filepath, '-frames:v', '1',
|
||||||
|
'-f', 'image2pipe', '-vcodec', 'png', 'pipe:1'],
|
||||||
|
capture_output=True, timeout=30, stdin=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout:
|
||||||
|
img = Image.open(BytesIO(result.stdout)).convert('RGB')
|
||||||
|
return img
|
||||||
|
logger.error(f"ffmpeg HEIC decode failed for {filepath}: {result.stderr.decode()[-200:]}")
|
||||||
|
except Exception as e2:
|
||||||
|
logger.error(f"ffmpeg fallback failed for {filepath}: {e2}")
|
||||||
|
raise RuntimeError(f"Cannot decode HEIC: {filepath}")
|
||||||
|
|
||||||
def process_video_thumbnail(filepath: str) -> Image.Image:
|
def process_video_thumbnail(filepath: str) -> Image.Image:
|
||||||
"""Extract a still frame from a video file as a PIL Image."""
|
"""Extract a still frame from a video file as a PIL Image."""
|
||||||
|
|||||||
Reference in New Issue
Block a user