- Add pillow-heif dependency for iPhone photo support - Auto-convert HEIC to JPEG (browsers can't display HEIC) - Graceful error if pillow-heif not installed - Handles RGBA/P mode conversion to RGB 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.8 KiB
HEIC Image Support Design - v1.4.2
Status: Ready for Implementation Type: Patch Release (backward compatible bug fix) Date: 2025-12-16
Problem Statement
iPhones save photos in HEIC format by default (since iOS 11). When users upload photos from iPhones to StarPunk, they receive "Invalid image format" errors because:
- HEIC is not in
ALLOWED_MIME_TYPES - Pillow cannot open HEIC files without the
pillow-heifplugin - iOS sometimes renames
.heicfiles to.jpegwithout converting, causing confusion
HEIC files cannot be displayed directly in browsers, so conversion to JPEG is required.
Design Overview
This is a minimal patch release with a single conceptual change: detect HEIC/HEIF images and convert them to JPEG before processing. The implementation requires:
- Dependency Addition: Add
pillow-heifto requirements.txt - Code Change: Modify
validate_image()instarpunk/media.pyto detect and convert HEIC
Implementation Specification
1. Dependency Update
File: /home/phil/Projects/starpunk/requirements.txt
Add after Pillow:
# HEIC/HEIF Support (v1.4.2 - iPhone photos)
pillow-heif==0.18.*
Note: pillow-heif automatically registers with Pillow on import, enabling HEIC support.
2. Code Changes
File: /home/phil/Projects/starpunk/starpunk/media.py
2.1 Add Import (top of file, after existing imports)
# HEIC/HEIF support - import registers with Pillow automatically
try:
import pillow_heif
pillow_heif.register_heif_opener()
HEIC_SUPPORTED = True
except ImportError:
HEIC_SUPPORTED = False
Rationale: Conditional import allows graceful degradation if pillow-heif is not installed (e.g., during development).
2.2 Modify validate_image() Function
Insert HEIC detection and conversion immediately after the Pillow image verification (line ~99), before the format check (line ~104).
Insert after line 99 (after img = Image.open(io.BytesIO(file_data))):
# HEIC/HEIF conversion (v1.4.2)
# HEIC cannot be displayed in browsers, convert to JPEG
if img.format in ('HEIF', 'HEIC'):
if not HEIC_SUPPORTED:
raise ValueError(
"HEIC/HEIF images require pillow-heif library. "
"Please convert to JPEG before uploading."
)
# Convert HEIC to JPEG in memory
output = io.BytesIO()
# Convert to RGB if needed (HEIC may have alpha channel)
if img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
img.save(output, format='JPEG', quality=95)
output.seek(0)
# Re-open as JPEG for further processing
file_data = output.getvalue()
img = Image.open(io.BytesIO(file_data))
Modify the return statement to return the potentially converted file_data:
The current function signature returns Tuple[str, int, int] (mime_type, width, height). We need to also return the converted bytes when HEIC conversion occurs.
Change return type (line 84):
def validate_image(file_data: bytes, filename: str) -> Tuple[bytes, str, int, int]:
Change return statement (line 138):
return file_data, mime_type, width, height
2.3 Update save_media() to Handle New Return Value
Modify line 400 in save_media():
# Validate image (returns 4-tuple with potentially converted bytes)
try:
file_data, mime_type, orig_width, orig_height = validate_image(file_data, filename)
except ValueError as e:
Note: The file_data variable is already in scope, so this reassignment handles the HEIC conversion case transparently.
Design Decisions
D1: Convert at Validation Time
Decision: Convert HEIC to JPEG during validate_image() rather than in a separate step.
Rationale:
- Keeps the change minimal (single function modification)
- Converted data flows naturally through existing
optimize_image()pipeline - No new function signatures or abstractions needed
- Validation is the logical place to normalize input formats
D2: Convert to JPEG (not WebP or PNG)
Decision: Convert HEIC to JPEG format.
Rationale:
- JPEG has universal browser support
- HEIC photos are typically photographic content, which JPEG handles well
- Quality 95 preserves detail while reducing file size
- Consistent with existing JPEG optimization pipeline
D3: Graceful Degradation
Decision: Use conditional import with HEIC_SUPPORTED flag.
Rationale:
- Allows code to work without pillow-heif during development
- Provides clear error message if HEIC upload attempted without library
- No runtime crash if dependency missing
D4: Quality Setting
Decision: Use quality=95 for HEIC to JPEG conversion.
Rationale:
- Preserves most detail from the original
- Subsequent
optimize_image()call will further compress if needed - Matches existing optimization tier behavior for high-quality inputs
Testing Requirements
Unit Tests
Add to existing media tests in /home/phil/Projects/starpunk/tests/test_media.py:
-
Test HEIC detection and conversion
- Upload valid HEIC file
- Verify output is JPEG format
- Verify dimensions preserved
-
Test HEIC with alpha channel
- Upload HEIC with transparency
- Verify conversion to RGB (no alpha in JPEG)
-
Test error handling without pillow-heif
- Mock
HEIC_SUPPORTED = False - Verify appropriate error message
- Mock
Test Files
A sample HEIC file is needed for testing. Options:
- Create programmatically using pillow-heif
- Download from a public test file repository
- Use iPhone simulator to generate
Migration Notes
- Database: No changes required
- Configuration: No changes required
- Existing uploads: Not affected (HEIC was previously rejected)
- Backward compatibility: Fully backward compatible
Files Changed
| File | Change |
|---|---|
requirements.txt |
Add pillow-heif==0.18.* |
starpunk/media.py |
Add HEIC import, modify validate_image() |
starpunk/__init__.py |
Update __version__ to "1.4.2" |
CHANGELOG.md |
Add v1.4.2 release notes |
tests/test_media.py |
Add HEIC test cases |
Changelog Entry
## [1.4.2] - 2025-12-XX
### Added
- HEIC/HEIF image format support for iPhone photo uploads
- Automatic HEIC to JPEG conversion (browsers cannot display HEIC)
### Dependencies
- Added `pillow-heif` for HEIC image processing
Implementation Checklist
- Add
pillow-heif==0.18.*to requirements.txt - Add HEIC import and registration to media.py
- Modify
validate_image()return type to include bytes - Add HEIC detection and conversion logic to
validate_image() - Update
save_media()to handle new return value - Update
__version__to "1.4.2" - Add HEIC test cases
- Update CHANGELOG.md
- Run full test suite