Spaces:
Running
Running
| """Catalog API routes — query and manage generated images.""" | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from fastapi import APIRouter, HTTPException | |
| from fastapi.responses import FileResponse | |
| from content_engine.models.schemas import ImageResponse | |
| router = APIRouter(prefix="/api", tags=["catalog"]) | |
| _catalog = None | |
| def init_routes(catalog): | |
| """Initialize route dependencies.""" | |
| global _catalog | |
| _catalog = catalog | |
| async def list_images( | |
| character_id: str | None = None, | |
| content_rating: str | None = None, | |
| template_id: str | None = None, | |
| is_approved: bool | None = None, | |
| is_published: bool | None = None, | |
| pose: str | None = None, | |
| outfit: str | None = None, | |
| emotion: str | None = None, | |
| limit: int = 50, | |
| offset: int = 0, | |
| ): | |
| """Search and list generated images with optional filters.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| images = await _catalog.search( | |
| character_id=character_id, | |
| content_rating=content_rating, | |
| template_id=template_id, | |
| is_approved=is_approved, | |
| is_published=is_published, | |
| pose=pose, | |
| outfit=outfit, | |
| emotion=emotion, | |
| limit=limit, | |
| offset=offset, | |
| ) | |
| return [ | |
| ImageResponse( | |
| id=img.id, | |
| character_id=img.character_id, | |
| template_id=img.template_id, | |
| content_rating=img.content_rating, | |
| file_path=img.file_path, | |
| seed=img.seed, | |
| pose=img.pose, | |
| outfit=img.outfit, | |
| emotion=img.emotion, | |
| camera_angle=img.camera_angle, | |
| lighting=img.lighting, | |
| scene=img.scene, | |
| quality_score=img.quality_score, | |
| is_approved=img.is_approved, | |
| is_published=img.is_published, | |
| created_at=img.created_at, | |
| ) | |
| for img in images | |
| ] | |
| async def get_image(image_id: str): | |
| """Get a single image by ID.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| img = await _catalog.get_image(image_id) | |
| if img is None: | |
| raise HTTPException(404, f"Image not found: {image_id}") | |
| return ImageResponse( | |
| id=img.id, | |
| character_id=img.character_id, | |
| template_id=img.template_id, | |
| content_rating=img.content_rating, | |
| file_path=img.file_path, | |
| seed=img.seed, | |
| pose=img.pose, | |
| outfit=img.outfit, | |
| emotion=img.emotion, | |
| camera_angle=img.camera_angle, | |
| lighting=img.lighting, | |
| scene=img.scene, | |
| quality_score=img.quality_score, | |
| is_approved=img.is_approved, | |
| is_published=img.is_published, | |
| created_at=img.created_at, | |
| ) | |
| async def serve_image_file(image_id: str): | |
| """Serve the actual image file for display in the UI.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| img = await _catalog.get_image(image_id) | |
| if img is None: | |
| raise HTTPException(404, f"Image not found: {image_id}") | |
| file_path = Path(img.file_path) | |
| if not file_path.exists(): | |
| raise HTTPException(404, f"Image file not found on disk") | |
| ext = file_path.suffix.lower() | |
| media_type = "video/mp4" if ext == ".mp4" else "video/webm" if ext == ".webm" else "image/png" | |
| return FileResponse( | |
| file_path, | |
| media_type=media_type, | |
| headers={"Cache-Control": "public, max-age=3600"}, | |
| ) | |
| async def download_image_file(image_id: str): | |
| """Download the image file as an attachment.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| img = await _catalog.get_image(image_id) | |
| if img is None: | |
| raise HTTPException(404, f"Image not found: {image_id}") | |
| file_path = Path(img.file_path) | |
| if not file_path.exists(): | |
| raise HTTPException(404, "Image file not found on disk") | |
| return FileResponse( | |
| file_path, | |
| media_type="image/png", | |
| filename=file_path.name, | |
| ) | |
| async def approve_image(image_id: str): | |
| """Mark an image as approved for publishing.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| success = await _catalog.approve_image(image_id) | |
| if not success: | |
| raise HTTPException(404, f"Image not found: {image_id}") | |
| return {"status": "approved", "image_id": image_id} | |
| async def delete_image(image_id: str): | |
| """Delete an image from the catalog and disk.""" | |
| if _catalog is None: | |
| raise HTTPException(503, "Catalog not initialized") | |
| success = await _catalog.delete_image(image_id) | |
| if not success: | |
| raise HTTPException(404, f"Image not found: {image_id}") | |
| return {"status": "deleted", "image_id": image_id} | |