feat: Enhance Park Detail Endpoint with Media URL Service Integration

- Updated ParkDetailOutputSerializer to utilize MediaURLService for generating Cloudflare URLs and friendly URLs for park photos.
- Added support for multiple lookup methods (ID and slug) in the park detail endpoint.
- Improved documentation for the park detail endpoint, including request properties and response structure.
- Created MediaURLService for generating SEO-friendly URLs and handling Cloudflare image URLs.
- Comprehensive updates to frontend documentation to reflect new endpoint capabilities and usage examples.
- Added detailed park detail endpoint documentation, including request and response structures, field descriptions, and usage examples.
This commit is contained in:
pacnpal
2025-08-31 16:45:47 -04:00
parent 91906e0d57
commit 0fd6dc2560
12 changed files with 1530 additions and 380 deletions

View File

@@ -0,0 +1,149 @@
"""
Media URL service for generating friendly URLs.
This service provides utilities for generating SEO-friendly URLs for media files
while maintaining compatibility with Cloudflare Images.
"""
import re
from typing import Optional, Dict, Any
from django.utils.text import slugify
from django.conf import settings
class MediaURLService:
"""Service for generating and parsing friendly media URLs."""
@staticmethod
def generate_friendly_filename(caption: str, photo_id: int, extension: str = "jpg") -> str:
"""
Generate a friendly filename from photo caption and ID.
Args:
caption: Photo caption
photo_id: Photo database ID
extension: File extension (default: jpg)
Returns:
Friendly filename like "beautiful-park-entrance-123.jpg"
"""
if caption:
# Clean and slugify the caption
slug = slugify(caption)
# Limit length to avoid overly long URLs
if len(slug) > 50:
slug = slug[:50].rsplit('-', 1)[0] # Cut at word boundary
return f"{slug}-{photo_id}.{extension}"
else:
return f"photo-{photo_id}.{extension}"
@staticmethod
def generate_park_photo_url(park_slug: str, caption: str, photo_id: int, variant: str = "public") -> str:
"""
Generate a friendly URL for a park photo.
Args:
park_slug: Park slug
caption: Photo caption
photo_id: Photo database ID
variant: Image variant (public, thumbnail, medium, large)
Returns:
Friendly URL like "/parks/cedar-point/photos/beautiful-entrance-123.jpg"
"""
filename = MediaURLService.generate_friendly_filename(caption, photo_id)
# Add variant to filename if not public
if variant != "public":
name, ext = filename.rsplit('.', 1)
filename = f"{name}-{variant}.{ext}"
return f"/parks/{park_slug}/photos/{filename}"
@staticmethod
def generate_ride_photo_url(park_slug: str, ride_slug: str, caption: str, photo_id: int, variant: str = "public") -> str:
"""
Generate a friendly URL for a ride photo.
Args:
park_slug: Park slug
ride_slug: Ride slug
caption: Photo caption
photo_id: Photo database ID
variant: Image variant
Returns:
Friendly URL like "/parks/cedar-point/rides/millennium-force/photos/first-drop-456.jpg"
"""
filename = MediaURLService.generate_friendly_filename(caption, photo_id)
if variant != "public":
name, ext = filename.rsplit('.', 1)
filename = f"{name}-{variant}.{ext}"
return f"/parks/{park_slug}/rides/{ride_slug}/photos/{filename}"
@staticmethod
def parse_photo_filename(filename: str) -> Optional[Dict[str, Any]]:
"""
Parse a friendly filename to extract photo ID and variant.
Args:
filename: Filename like "beautiful-entrance-123-thumbnail.jpg"
Returns:
Dict with photo_id and variant, or None if parsing fails
"""
# Remove extension
name = filename.rsplit('.', 1)[0]
# Check for variant suffix
variant = "public"
variant_patterns = ["thumbnail", "medium", "large"]
for v in variant_patterns:
if name.endswith(f"-{v}"):
variant = v
name = name[:-len(f"-{v}")]
break
# Extract photo ID (should be the last number)
match = re.search(r'-(\d+)$', name)
if match:
photo_id = int(match.group(1))
return {
"photo_id": photo_id,
"variant": variant
}
return None
@staticmethod
def get_cloudflare_url_with_fallback(cloudflare_image, variant: str = "public") -> Optional[str]:
"""
Get Cloudflare URL with fallback handling.
Args:
cloudflare_image: CloudflareImage instance
variant: Desired variant
Returns:
Cloudflare URL or None
"""
if not cloudflare_image:
return None
try:
# Try the specific variant first
url = cloudflare_image.get_url(variant)
if url:
return url
# Fallback to public URL
if variant != "public":
return cloudflare_image.public_url
except Exception:
pass
return None