v1.3.1 "Syndicate Tags": - RSS/Atom/JSON Feed category/tag support v1.4.0 "Media": - Micropub media endpoint (W3C compliant) - Large image support (>10MB auto-resize) - Enhanced feed media (image variants, full Media RSS) Also adds tag-filtered feeds to backlog at medium priority. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
11 KiB
StarPunk v1.4.0 Release
Status: Planning Codename: "Media" Focus: Micropub Media Endpoint, Large Image Support, Enhanced Feed Media
Overview
This minor release significantly enhances StarPunk's media capabilities with three major features:
- Micropub Media Endpoint - W3C-compliant media upload via Micropub clients
- Large Image Support - Accept and resize images larger than 10MB
- Enhanced Feed Media - Multiple image sizes and complete Media RSS implementation
Features
1. Micropub Media Endpoint
Implement the W3C Micropub media endpoint specification, enabling Micropub clients to upload photos, audio, and video files.
Specification Compliance (W3C Micropub)
Endpoint: POST /micropub/media
Request Format:
- Content-Type:
multipart/form-data - Single file part named
file - Authorization: Bearer token with
mediaorcreatescope
Response:
201 CreatedwithLocationheader containing the file URL- URL should be unguessable (UUID-based, which we already do)
Example Request:
POST /micropub/media HTTP/1.1
Authorization: Bearer xxx
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg
[binary data]
------WebKitFormBoundary--
Example Response:
HTTP/1.1 201 Created
Location: https://example.com/media/2025/01/abc123-def456.jpg
Configuration Discovery
Update q=config response to advertise media endpoint:
{
"media-endpoint": "https://example.com/micropub/media",
"syndicate-to": []
}
Photo Property Support
Enable photo property in Micropub create requests:
URL Reference (photo already uploaded or external):
h=entry&content=Hello&photo=https://example.com/media/2025/01/abc.jpg
JSON with Alt Text:
{
"type": ["h-entry"],
"properties": {
"content": ["Hello world"],
"photo": [{
"value": "https://example.com/media/2025/01/abc.jpg",
"alt": "A beautiful sunset"
}]
}
}
Multiple Photos:
{
"type": ["h-entry"],
"properties": {
"content": ["Photo gallery"],
"photo": [
"https://example.com/media/2025/01/photo1.jpg",
"https://example.com/media/2025/01/photo2.jpg"
]
}
}
Implementation Details
New Route: starpunk/routes/micropub.py
@bp.route('/media', methods=['POST'])
def media_endpoint():
# Verify bearer token (media or create scope)
# Extract file from multipart/form-data
# Call save_media() from media.py
# Return 201 with Location header
Modified: starpunk/micropub.py
- Update
get_micropub_config()to return media-endpoint URL - Add
extract_photos()function for photo property parsing - Modify
handle_create()to process photo URLs and attach to note
Scope Validation:
- Media endpoint requires
mediaorcreatescope - Photo property in create requests requires
createscope
2. Large Image Support (>10MB)
Remove the 10MB file size rejection and instead automatically resize large images to fit within acceptable limits.
Current Behavior (v1.2.0)
- Files >10MB are rejected with error
- Files ≤10MB are accepted and resized if >2048px
New Behavior (v1.4.0)
- Files of any reasonable size accepted (new limit: 50MB)
- Images >10MB are automatically resized more aggressively
- Final output always ≤10MB after optimization
Resize Strategy
| Input Size | Max Dimension | Quality | Target Output |
|---|---|---|---|
| ≤10MB | 2048px | 95% | ≤5MB typical |
| 10-25MB | 1600px | 90% | ≤5MB target |
| 25-50MB | 1280px | 85% | ≤5MB target |
| >50MB | Rejected | - | Error message |
Implementation Details
Modified: starpunk/media.py
def validate_image(file_data, filename):
"""Updated validation with higher limit."""
MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB (was 10MB)
# ... rest of validation
def optimize_image(image_data, original_size=None):
"""Enhanced optimization based on input size."""
# Determine resize parameters based on original_size
# More aggressive resize for larger files
# Quality reduction for very large files
# Iterative optimization if output still too large
Quality Iteration: If first pass produces >10MB output:
- Reduce max dimension by 20%
- Reduce quality by 5%
- Repeat until ≤10MB or min quality (70%) reached
User Experience
- No rejection for reasonable photo sizes
- Transparent optimization (user doesn't need to pre-resize)
- Warning in response if significant quality reduction applied
- Original dimensions preserved in EXIF if possible
3. Enhanced Feed Media Support
Implement ADR-059 Phase A: Multiple image sizes and complete Media RSS implementation.
Multiple Image Sizes (Thumbnails)
Generate multiple renditions on upload:
| Variant | Dimensions | Use Case |
|---|---|---|
thumb |
150×150 (square crop) | Thumbnails, previews |
small |
320px width | Mobile, low bandwidth |
medium |
640px width | Standard display |
large |
1280px width | High-res display |
original |
As uploaded (≤2048px) | Full quality |
Database Schema (new migration):
CREATE TABLE media_variants (
id INTEGER PRIMARY KEY AUTOINCREMENT,
media_id INTEGER NOT NULL,
variant_type TEXT NOT NULL, -- 'thumb', 'small', 'medium', 'large', 'original'
path TEXT NOT NULL,
width INTEGER NOT NULL,
height INTEGER NOT NULL,
size_bytes INTEGER NOT NULL,
FOREIGN KEY (media_id) REFERENCES media(id) ON DELETE CASCADE,
UNIQUE(media_id, variant_type)
);
CREATE INDEX idx_media_variants_media ON media_variants(media_id);
Storage Structure:
/media/2025/01/
├── abc123.jpg # Original/large
├── abc123_medium.jpg # 640px
├── abc123_small.jpg # 320px
└── abc123_thumb.jpg # 150×150
Complete Media RSS Implementation
Enhance RSS feeds with full Media RSS specification support:
<item>
<title>My Photo Post</title>
<media:group>
<media:content url="https://.../large.jpg"
type="image/jpeg"
medium="image"
isDefault="true"
width="1280" height="960"
fileSize="245760"/>
<media:content url="https://.../medium.jpg"
type="image/jpeg"
medium="image"
width="640" height="480"
fileSize="98304"/>
<media:content url="https://.../small.jpg"
type="image/jpeg"
medium="image"
width="320" height="240"
fileSize="32768"/>
</media:group>
<media:thumbnail url="https://.../thumb.jpg"
width="150" height="150"/>
<media:title type="plain">Image caption here</media:title>
</item>
New Media RSS Elements:
<media:group>- Container for multiple renditions<media:content>with full attributes (width, height, fileSize, medium, isDefault)<media:thumbnail>with dimensions<media:title>for captions (type="plain")
Enhanced JSON Feed Attachments
{
"items": [{
"id": "...",
"image": "https://.../large.jpg",
"attachments": [
{
"url": "https://.../large.jpg",
"mime_type": "image/jpeg",
"title": "Image caption",
"size_in_bytes": 245760
}
],
"_starpunk": {
"media_variants": {
"thumb": "https://.../thumb.jpg",
"small": "https://.../small.jpg",
"medium": "https://.../medium.jpg",
"large": "https://.../large.jpg"
}
}
}]
}
ATOM Feed Enclosures
Add proper enclosure links to ATOM entries:
<entry>
<link rel="enclosure"
type="image/jpeg"
length="245760"
title="Image caption"
href="https://.../large.jpg"/>
</entry>
Implementation Phases
Phase 1: Large Image Support (4-6 hours)
- Update
validate_image()with 50MB limit - Implement tiered resize strategy in
optimize_image() - Add iterative quality reduction
- Update tests for new limits
Phase 2: Image Variants (8-12 hours)
- Create migration for
media_variantstable - Implement variant generation in
save_media() - Update
get_note_media()to include variants - Add variant serving routes
- Update templates for responsive images
Phase 3: Micropub Media Endpoint (6-8 hours)
- Create
/micropub/mediaroute - Update
q=configresponse - Implement
photoproperty parsing - Update
handle_create()for photo attachment - Add scope validation for media uploads
Phase 4: Enhanced Feed Media (6-8 hours)
- Update RSS generator for
<media:group>and variants - Update JSON Feed for variants in
_starpunkextension - Add ATOM enclosure links
- Update feed tests
Phase 5: Testing & Documentation (4-6 hours)
- Comprehensive test suite for all new features
- Update API documentation
- Update architecture documentation
- Update CHANGELOG
Total Estimated Effort: 28-40 hours
Technical Notes
Backward Compatibility
- Existing media continues to work (no variants, single size)
- Feeds gracefully handle notes with/without variants
- Micropub clients without media support continue to work
Performance Considerations
- Variant generation is synchronous on upload (acceptable for single-user)
- Consider background processing for v1.5.0 if needed
- Cache headers on all media variants (1 year immutable)
Storage Impact
- ~4x storage per image (thumb + small + medium + large)
- Consider cleanup of unused uploaded media
- Document storage requirements in deployment guide
Security
- Media endpoint requires valid bearer token
- File type validation (images only for v1.4.0)
- UUID filenames prevent enumeration
- Path traversal protection maintained
Out of Scope (Deferred)
v1.5.0 or Later
- Audio/podcast support (ADR-059 Phase B)
- Video support (ADR-059 Phase C)
- Micropub update/delete operations
- Background media processing
- CDN integration
Not Planned
- External media hosting (Cloudinary, etc.)
- Media transcoding
- Live photo/HEIC support
Dependencies
- v1.3.x complete (tags feature)
- Pillow library (already installed)
- No new external dependencies
Success Criteria
- Micropub clients can upload media via media endpoint
- Photos attached to notes via Micropub
photoproperty - Images >10MB accepted and resized appropriately
- All image variants generated and served
- Feed readers see proper Media RSS with variants
- All existing tests continue to pass
- New comprehensive test coverage for media features
Related Documentation
- W3C Micropub Specification
- Media RSS Specification
- ADR-057: Media Attachment Model
- ADR-058: Image Optimization Strategy
- ADR-059: Full Feed Media Standardization
docs/architecture/syndication-architecture.md