All checks were successful
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 12s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 9s
CI/CD Pipeline / VM Test - performance (push) Successful in 9s
CI/CD Pipeline / VM Test - security (push) Successful in 9s
CI/CD Pipeline / Backend Linting (push) Successful in 3s
CI/CD Pipeline / Frontend Linting (push) Successful in 24s
CI/CD Pipeline / Nix Flake Check (push) Successful in 53s
CI/CD Pipeline / CI Summary (push) Successful in 1s
CI/CD Pipeline / VM Test - backend-integration (pull_request) Successful in 2s
CI/CD Pipeline / VM Test - full-stack (pull_request) Successful in 2s
CI/CD Pipeline / VM Test - performance (pull_request) Successful in 2s
CI/CD Pipeline / VM Test - security (pull_request) Successful in 2s
CI/CD Pipeline / Backend Linting (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Linting (pull_request) Successful in 16s
CI/CD Pipeline / Nix Flake Check (pull_request) Successful in 38s
CI/CD Pipeline / CI Summary (pull_request) Successful in 0s
104 lines
2.7 KiB
Python
104 lines
2.7 KiB
Python
"""Image serving with quality-based thumbnail selection."""
|
|
|
|
from fastapi import HTTPException, status
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
from app.database.models.image import Image
|
|
|
|
|
|
def get_thumbnail_path(image: Image, quality: str) -> str:
|
|
"""
|
|
Get thumbnail path for specified quality level.
|
|
|
|
Args:
|
|
image: Image model instance
|
|
quality: Quality level ('low', 'medium', 'high', 'original')
|
|
|
|
Returns:
|
|
Storage path to thumbnail
|
|
|
|
Raises:
|
|
ValueError: If quality level is invalid
|
|
"""
|
|
if quality == "original":
|
|
return image.storage_path
|
|
|
|
# Get thumbnail paths from metadata
|
|
thumbnails = image.image_metadata.get("thumbnails", {})
|
|
|
|
# Map quality to thumbnail size
|
|
if quality == "low":
|
|
thumbnail_path = thumbnails.get("low")
|
|
elif quality == "medium":
|
|
thumbnail_path = thumbnails.get("medium")
|
|
elif quality == "high":
|
|
thumbnail_path = thumbnails.get("high")
|
|
else:
|
|
raise ValueError(f"Invalid quality level: {quality}")
|
|
|
|
# Fall back to original if thumbnail doesn't exist
|
|
if not thumbnail_path:
|
|
return image.storage_path
|
|
|
|
return thumbnail_path
|
|
|
|
|
|
async def serve_image_with_quality(
|
|
image: Image, quality: str = "medium", filename: str | None = None
|
|
) -> StreamingResponse:
|
|
"""
|
|
Serve image with specified quality level.
|
|
|
|
Args:
|
|
image: Image model instance
|
|
quality: Quality level ('low', 'medium', 'high', 'original')
|
|
filename: Optional custom filename for download
|
|
|
|
Returns:
|
|
StreamingResponse with image data
|
|
|
|
Raises:
|
|
HTTPException: If image cannot be served
|
|
"""
|
|
from app.images.download import download_single_image
|
|
|
|
try:
|
|
# Get appropriate thumbnail path
|
|
storage_path = get_thumbnail_path(image, quality)
|
|
|
|
# Use original filename if not specified
|
|
if filename is None:
|
|
filename = image.filename
|
|
|
|
# Serve the image
|
|
return await download_single_image(storage_path, filename)
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(e),
|
|
) from e
|
|
except Exception as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Failed to serve image: {str(e)}",
|
|
) from e
|
|
|
|
|
|
def determine_quality_from_speed(speed_mbps: float) -> str:
|
|
"""
|
|
Determine appropriate quality level based on connection speed.
|
|
|
|
Args:
|
|
speed_mbps: Connection speed in Mbps
|
|
|
|
Returns:
|
|
Quality level string
|
|
"""
|
|
if speed_mbps < 1.0:
|
|
return "low"
|
|
elif speed_mbps < 5.0:
|
|
return "medium"
|
|
else:
|
|
return "high"
|