feat: v1.4.0 Phase 4 - Enhanced Feed Media

Implement Phase 4 of v1.4.0 Media release - Enhanced Feed Media support.

RSS Feed Enhancements (starpunk/feeds/rss.py):
- Wrap size variants in <media:group> elements
- Add <media:content> for large/medium/small variants with attributes:
  url, type, medium, isDefault, width, height, fileSize
- Add <media:thumbnail> for thumb variant with dimensions
- Add <media:title type="plain"> for image captions
- Implement isDefault logic: largest available variant (large→medium→small fallback)
- Maintain backwards compatibility for media without variants (legacy fallback)

JSON Feed Enhancements (starpunk/feeds/json_feed.py):
- Add _starpunk.about URL (configurable via STARPUNK_ABOUT_URL config)
- Add _starpunk.media_variants array with variant data when variants exist
- Each variant entry includes: url, width, height, size_in_bytes, mime_type

ATOM Feed Enhancements (starpunk/feeds/atom.py):
- Add title attribute to enclosure links for captions
- Keep simple (no variants in ATOM per design decision)

Test Updates (tests/test_feeds_rss.py):
- Update streaming media test to search descendants for media:content
- Now inside media:group for images with variants (v1.4.0 behavior)

Per design document: /docs/design/v1.4.0/media-implementation-design.md
Following ADR-059: Full Feed Media Standardization

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-16 07:47:56 -07:00
parent c64feaea23
commit 83dc488457
4 changed files with 102 additions and 16 deletions

View File

@@ -402,7 +402,7 @@ class TestRSSStreamingMedia:
assert enclosure is not None
def test_rss_streaming_includes_media_elements(self, app, note_with_single_media):
"""Streaming RSS should include media:content and media:thumbnail"""
"""Streaming RSS should include media:content and media:thumbnail (v1.4.0 with variants)"""
with app.app_context():
generator = generate_rss_streaming(
site_url="https://example.com",
@@ -419,8 +419,10 @@ class TestRSSStreamingMedia:
channel = root.find("channel")
item = channel.find("item")
media_content = item.find("media:content", namespaces)
media_thumbnail = item.find("media:thumbnail", namespaces)
# v1.4.0: media:content is now inside media:group for images with variants
# Use // to search descendants, not direct children
media_content = item.find(".//media:content", namespaces)
media_thumbnail = item.find(".//media:thumbnail", namespaces)
assert media_content is not None
assert media_thumbnail is not None