Files
StarPunk/docs/projectplan/v1.4.0/RELEASE.md
Phil Skentelbery 41b65703f9 docs: Add v1.3.1 and v1.4.0 release definitions
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>
2025-12-10 12:00:56 -07:00

11 KiB
Raw Blame History

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:

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 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

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):

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_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