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>
402 lines
11 KiB
Markdown
402 lines
11 KiB
Markdown
# 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:
|
||
|
||
1. **Micropub Media Endpoint** - W3C-compliant media upload via Micropub clients
|
||
2. **Large Image Support** - Accept and resize images larger than 10MB
|
||
3. **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 `media` or `create` scope
|
||
|
||
**Response**:
|
||
- `201 Created` with `Location` header containing the file URL
|
||
- URL should be unguessable (UUID-based, which we already do)
|
||
|
||
**Example Request**:
|
||
```http
|
||
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
|
||
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:
|
||
```json
|
||
{
|
||
"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**:
|
||
```json
|
||
{
|
||
"type": ["h-entry"],
|
||
"properties": {
|
||
"content": ["Hello world"],
|
||
"photo": [{
|
||
"value": "https://example.com/media/2025/01/abc.jpg",
|
||
"alt": "A beautiful sunset"
|
||
}]
|
||
}
|
||
}
|
||
```
|
||
|
||
**Multiple Photos**:
|
||
```json
|
||
{
|
||
"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`
|
||
```python
|
||
@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 `media` or `create` scope
|
||
- Photo property in create requests requires `create` scope
|
||
|
||
---
|
||
|
||
### 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`
|
||
|
||
```python
|
||
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:
|
||
1. Reduce max dimension by 20%
|
||
2. Reduce quality by 5%
|
||
3. 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):
|
||
```sql
|
||
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:
|
||
|
||
```xml
|
||
<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
|
||
|
||
```json
|
||
{
|
||
"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:
|
||
```xml
|
||
<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_variants` table
|
||
- 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/media` route
|
||
- Update `q=config` response
|
||
- Implement `photo` property 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 `_starpunk` extension
|
||
- 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
|
||
|
||
1. Micropub clients can upload media via media endpoint
|
||
2. Photos attached to notes via Micropub `photo` property
|
||
3. Images >10MB accepted and resized appropriately
|
||
4. All image variants generated and served
|
||
5. Feed readers see proper Media RSS with variants
|
||
6. All existing tests continue to pass
|
||
7. New comprehensive test coverage for media features
|
||
|
||
## Related Documentation
|
||
|
||
- [W3C Micropub Specification](https://www.w3.org/TR/micropub/)
|
||
- [Media RSS Specification](https://www.rssboard.org/media-rss)
|
||
- ADR-057: Media Attachment Model
|
||
- ADR-058: Image Optimization Strategy
|
||
- ADR-059: Full Feed Media Standardization
|
||
- `docs/architecture/syndication-architecture.md`
|