"""Galerie publique -- accessible sans authentification."""
import asyncio
import logging
from datetime import datetime
from pathlib import Path
import httpx
from fastapi import APIRouter, Request, Query
from fastapi.responses import HTMLResponse, RedirectResponse, Response
from fastapi.templating import Jinja2Templates
logger = logging.getLogger(__name__)
router = APIRouter()
_templates = Jinja2Templates(directory=Path(__file__).parent.parent.parent / "frontend" / "templates")
@router.get("/", response_class=HTMLResponse)
async def index(request: Request):
return RedirectResponse(url="/gallery")
@router.get("/gallery", response_class=HTMLResponse)
async def gallery_page(request: Request):
cfg = request.app.state.config
if not cfg.gallery.public_enabled:
return HTMLResponse("
Galerie desactivee
", status_code=403)
return _templates.TemplateResponse(request, "public/gallery.html", {"config": cfg})
@router.get("/api/gallery/photos")
async def api_gallery_photos(
request: Request,
page: int = Query(default=1, ge=1),
limit: int = Query(default=24, ge=1, le=100),
):
pb = request.app.state.photobooth_service
all_photos = await pb.get_media_collection(limit=500)
photos = [p for p in all_photos if _is_image(p)]
total = len(photos)
start = (page - 1) * limit
end = start + limit
page_photos = photos[start:end]
for p in page_photos:
pid = _get_id(p)
p["full_url"] = pb.media_url(pid)
p["thumb_url"] = pb.thumbnail_url(pid)
p["download_url"] = f"/api/gallery/download/{pid}"
return {
"photos": page_photos,
"total": total,
"page": page,
"pages": max(1, (total + limit - 1) // limit),
}
@router.get("/api/gallery/download/{photo_id}")
async def download_photo(request: Request, photo_id: str):
"""Proxy la photo full-res avec un nom de fichier lie a l'evenement en cours."""
pb = request.app.state.photobooth_service
cfg = request.app.state.config
event_svc = getattr(request.app.state, "event_service", None)
img_url = pb.media_url(photo_id)
slug = cfg.event.slug or "photomaton"
date_str = datetime.now().strftime("%Y-%m-%d")
filename = f"{slug}_By_LSDW_{date_str}.jpg"
try:
async with httpx.AsyncClient(timeout=30.0) as client:
r = await client.get(img_url)
r.raise_for_status()
if event_svc:
asyncio.create_task(event_svc.increment(slug, "downloads"))
content_type = r.headers.get("content-type", "image/jpeg")
return Response(
content=r.content,
media_type=content_type,
headers={
"Content-Disposition": f'attachment; filename="{filename}"',
"Content-Length": str(len(r.content)),
"Cache-Control": "no-cache",
},
)
except Exception as e:
logger.warning("Download proxy echoue (%s), fallback redirect: %s", photo_id, e)
return RedirectResponse(url=img_url)
def _is_image(item: dict) -> bool:
t = item.get("type", item.get("mediaitem_type", "image"))
return str(t).lower() in ("image", "still", "photo")
def _get_id(item: dict) -> str:
return str(item.get("id", item.get("filename", item.get("uid", ""))))